Sei sulla pagina 1di 49

Introduccin:

Es una cosa muy simple, pero creo que es conveniente saber cmo hacerlo. Te explico que este cdigo es el que utilizo en mis foros a la hora de mostrar la fecha actual. Debido a que el foro est alojado en un servidor que reside en los Estados Unidos de Norteamrica (USA), dicho formato de fecha y hora es diferente, por ejemplo al usado en Espaa. En realidad, la fecha la puedo mostrar como quiera, pero si, por ejemplo, esa fecha (o formato) la quiero usar para acceder a una base de datos, si no est en el mismo formato del idioma en el que se ejecuta la base de datos, posiblemente no funcionar correctamente. El cdigo que utilizo es el siguiente, en este caso para mostrar dos fechas, una 15 das antes de la actual y otra la actual, ambas en formato universal, es decir, segn la hora universal (UTC) o de Greenwich (GMT):

Dim s As String s = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePatt ern txtDesde.Text = DateTime.Now.ToUniversalTime().Subtract(New TimeSpan(15, 0, 0, 0)).ToString(s) txtHasta.Text = DateTime.Now.ToUniversalTime().ToString(s)

En este caso, estoy usando la propiedad ShortDatePattern que vendra a ser algo as como MM/dd/yyyy, es deir, en formato mes/da/ao. Por otro lado, al usar el mtodo ToUniversalTime de la la propiedad Now, estoy obteniendo la fecha UTC, es decir, para que no utilice la del pacfico, que es la hora que tiene mi servidor, por ejemplo, estos valores que se muestran a continuacin se obtienen directamente del servidor, y en el caso de que ests viendo la pgina desde elGuille.info, (pulsa en el link para ir a esta misma pgina en elguille.info), vers que hay unas 8 horas de diferencia con la fecha universal (UTC/GMT), sin embargo, si lo ves desde MundoProgramacion.com, (pulsa en el link para ir a esta misma pgina en mundoprogramacion), esa fecha ser la de Espaa, ya que el servidor donde est alojado ese sitio reside en Espaa. La fecha en formato "normal", usando dd/MM/yyyy: 08/09/2011 Cdigo usado: Dim fecha1 As String = DateTime.Now.ToString("dd/MM/yyyy") Este mostrando la fecha y la hora con dd/MM/yyyy HH:mm: 08/09/2011 23:26 La fecha en formato "universal", usando dd/MM/yyyy: 08/09/2011 Cdigo usado: Dim fecha2 As String = DateTime.Now.ToUniversalTime().ToString("dd/MM/yyyy") Este mostrando la fecha y la hora con dd/MM/yyyy HH:mm: 08/09/2011 21:26

La fecha segn la cultura (en formato universal): 9/8/2011 Cdigo usado: Dim fecha3 As String = DateTime.Now.ToString(s) La fecha segn la cultura (segn la hora del servidor): 9/8/2011 Cdigo usado: Dim fecha4 As String = DateTime.Now.ToUniversalTime().ToString(s) En estos dos ltimos casos, el valor de "s" se obtiene con: s= System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePatter n

Nota: Internamente esta pgina est usando cdigo de Visual Basic .NET para mostrar esas fechas.

Cmo influye esto en una consulta de SQL Server?


Aunque parezca una tontera, al usar los valores desde y hasta del cdigo mostrado ms arriba, si esos valores los uso en una consulta de SQL Server para obtener los datos entre dos fechas, funcionar bien lo ejecute donde lo ejecute, pero si en lugar de usar la "fecha globalizada" usara el formato habitual: dd/MM/yyyy, slo me funcionara si la base de datos estuviese en un servidor configurado con ese "tipo" de fecha. La consulta usada es esta: (en realidad faltan cosas que indicar, pero lo importante es cmo usarla)
''' <summary> ''' Obtener los registros de una tabla entre dos fechas. ''' </summary> ''' <param name="desde">Fecha desde la que queremos los datos</param> ''' <param name="hasta">Fecha hasta la que queremos los datos</param> ''' <param name="max">El mximo de mensajes a devolver (entre 100 y 600, por defecto 400)</param> ''' <returns>Un DataTable con los mensajes hallados</returns> ''' <remarks></remarks> ''' <fecha>29/Dic/2005</fecha> Public Shared Function MensajesForo( _ ByVal desde As String, _ ByVal hasta As String, _ Optional ByVal max As Integer = 400) As DataTable '

Dim cnn As SqlConnection = Nothing Dim dt As New DataTable ' ' Dentro de un Try/Catch por si se produce un error Try If max > 600 OrElse max < 100 Then max = 400 ' Obtenemos la cadena de conexin adecuada Dim sConn As String = "data source=(local); " & _ "initial catalog=<nombre base de datos>; " & _ "user id=<usuario>; password=<contrasea>;" cnn = New SqlConnection(sConn) cnn.Open() ' Creamos el comando para la consulta Dim cmd As SqlCommand = New SqlCommand Dim sel As String = _ "SELECT TOP " & max & " <los campos a devolver> " & _ "FROM <nombre de la tabla> " & _ "WHERE ( Fecha >= @Fecha1 AND Fecha <= @Fecha2 ) " & _ "ORDER BY Fecha DESC" ' cmd.CommandText = sel cmd.Connection = cnn cmd.CommandType = CommandType.Text cmd.CommandTimeout = 90 ' Los parmetros usados en la cadena de la consulta cmd.Parameters.Add(New SqlParameter("@Fecha1", SqlDbType.DateTime)) cmd.Parameters.Add(New SqlParameter("@Fecha2", SqlDbType.DateTime)) cmd.Parameters("@Fecha1").Value = desde cmd.Parameters("@Fecha2").Value = hasta ' ' Creamos el dataAdapter y asignamos el comando de seleccin Dim da As New SqlDataAdapter da.SelectCommand = cmd ' Llenamos la tabla da.Fill(dt) Catch 'ex As Exception ' Si hay error, devolvemos un valor nulo. Return Nothing Finally ' Por si se produce un error, ' comprobamos si en realidad el objeto Connection est iniciado,

' de ser as, lo cerramos. If Not cnn Is Nothing Then cnn.Close() End If End Try ' ' Devolvemos el objeto DataTable con los datos de la consulta Return dt End Function

Y esto es todo, espero que te sea de utilidad. Nos vemos. Guillermo

El cdigo para VB .NET


Es el mostrado anteriormente.

El cdigo para C#
Este es el cdigo para asignar las cadenas de las fechas segn la cultura actual:
string s; s = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePatt ern; string desde = DateTime.Now.ToUniversalTime().Subtract(new TimeSpan(15, 0, 0, 0)).ToString(s); string hasta = DateTime.Now.ToUniversalTime().ToString(s);

Este es el cdigo para acceder a la base de datos y realizar la consulta:


/// <summary> /// Obtener los registros de una tabla entre dos fechas.

/// </summary> /// <param name="desde">Fecha desde la que queremos los datos</param> /// <param name="hasta">Fecha hasta la que queremos los datos</param> /// <param name="max">El mximo de mensajes a devolver (entre 100 y 600, por defecto 400)</param> /// <returns>Un DataTable con los mensajes hallados</returns> /// <remarks></remarks> /// <fecha>29/Dic/2005</fecha> public static DataTable MensajesForo(string desde, string hasta, int max) { // SqlConnection cnn = null; DataTable dt = new DataTable(); // // Dentro de un Try/Catch por si se produce un error try { if( max > 600 || max < 100 ) max = 400; string usuario = "<usuario>"; string passw = "<password>"; // Obtenemos la cadena de conexin adecuada string sConn = "data source=(local); " + "initial catalog=<nombre base de datos>; " + "user id=" + usuario + "; password=" + passw + ";"; cnn = new SqlConnection(sConn); cnn.Open(); // Creamos el comando para la consulta SqlCommand cmd = new SqlCommand(); string sel = "SELECT TOP " + max + " <los campos a mostrar> " + "FROM <nombre de la tabla> " + "WHERE ( Fecha >= @Fecha1 AND Fecha <= @Fecha2 ) " + "ORDER BY Fecha DESC"; // cmd.CommandText = sel; cmd.Connection = cnn; cmd.CommandType = CommandType.Text; cmd.CommandTimeout = 90; // Los parmetros usados en la cadena de la consulta

cmd.Parameters.Add(new SqlParameter("@Fecha1", SqlDbType.DateTime)); cmd.Parameters.Add(new SqlParameter("@Fecha2", SqlDbType.DateTime)); cmd.Parameters["@Fecha1"].Value = desde; cmd.Parameters["@Fecha2"].Value = hasta; // // Creamos el dataAdapter y asignamos el comando de seleccin SqlDataAdapter da = new SqlDataAdapter(); da.SelectCommand = cmd; // Llenamos la tabla da.Fill(dt); } catch(Exception ex) { // Si hay error, devolvemos un valor nulo. Console.WriteLine(ex.Message); return null; } finally { // Por si se produce un error, // comprobamos si en realidad el objeto Connection est iniciado, // de ser as, lo cerramos. if( cnn != null ) cnn.Close(); } // // Devolvemos el objeto DataTable con los datos de la consulta return dt; }

Crear un proyecto de instalacin (Setup) para Windows


Introduccin:

Aunque como sabemos con .NET Framework no es necesario crear programas (o proyectos) de instalacin, dado que este nuevo entrono facilita la forma de distribuir las aplicaciones mediante lo que se llama instalacin XCOPY (o FTP), es decir, podemos copiar nuestras aplicaciones creadas con cualquier lenguaje .NET simplemente copiando el directorio con el ejecutable y las libreras de las que depende al equipo en el que queremos usarlo y ya funcionar. Pero funcionar si el equipo de destino ya tiene instalada el "runtime" de .NET Framework, y esto es as porque ese runtime ya incluye todo lo que necesitamos para que nuestra aplicacin funcione. Aunque en ocasiones, es posible que adems de esas libreras "bsicas" (incluidas en el runtime del .NET), necesitemos copiar nuevas libreras, en ese caso simplemente copiaremos las libreras extras dentro del propio directorio de nuestra aplicacin y... todo funcionar! Esto es as porque no necesitamos registrar ningn componente para poder usarlo con nuestra aplicacin, por tanto... realmente no necesitamos crear ningn proyecto de instalacin, pero... si es eso lo que quieres hacer, para facilitarte las cosas, no es lo mismo tener que copiar cosas en una carpeta que distribuir un instalador y que sea el propio usuario el que se encargue del proceso de instalacin, as como de eliminar dicha instalacin cuando le apetezca. Tambin habr ocasiones en las que tengamos que instalar una librera "compartida", en esos casos, la creacin de un proyecto de instalacin nos facilitar el registro (o copia) de esa librera en el GAC (Global Assembly Cache, cach de ensamblado global) que es una carpeta especial usada por el .NET Framework en la que estarn todas las libreras compartidas del .NET Framework. Nota: Para que una librera se pueda usar de forma global, sta debe estar firmada con un Strong Name (nombre seguro). Si quieres saber ms cmo crear (o firmar) una librera con nombre seguro, puedes leer mi artculo sobre ese tema: Crear y registrar un ensamblado con nombre seguro Aviso importante: Una cosa que debes saber antes de seguir, es que creando un proyecto de instalacin, no se instala el runtime del .NET Framework. Cuando creamos el proyecto de instalacin, ste se encargar de instalar nuestra aplicacin y los dems ficheros (archivos) que le indiquemos, pero NO instalar el runtime. Lo repito para que quede claro y no haya dudas... El runtime (o redistribuible) de .NET Framework debemos instalarlo por nuestra cuenta ANTES de instalar nuestra aplicacin. Bien, sabiendo esto, sigamos con lo nuestro. Vamos a ver cmo crear un proyecto de instalacin. Para que quede la cosa clara, primero tendremos que tener abierto el proyecto con el ejecutable (o aplicacin) que queremos distribuir, (si quieres, puedes crear un

nuevo proyecto, simplemente para probar). Supongamos que el proyecto que queremos distribuir se llama: ClienteSetup y lo tenemos abierto en el Visual Studio .NET (la versin usada para este artculo es la versin 2002, pero los pasos a seguir sern los mismos para versiones posteriores).

Proyecto de instalacin usando el Asistente:


Del men Archivo, seleccionaremos la opcin Agregar proyecto>Nuevo proyecto..., se mostrar el cuadro de dilogo de aadir nuevo proyecto, del panel izquierdo selecciona Proyectos de instalacin e implementacin, en el panel derecho se mostrarn los posibles tipos de proyectos, seleccionaremos Asistente para proyectos de instalacin (despus veremos cmo crear un proyecto de instalacin sin usar el asistente). Este cuadro de dilogo podemos verlo en la siguiente figura (Figura 1):

Figura 1, cuadro de dilogo Agregar nuevo proyecto Se mostrar el Asistente para proyectos de instalacin (iremos viendo las distintas pantallas que se irn mostrando), en la figura 2, tenemos la primera de las 5 pantallas del asistente:

Figura 2, Primera pantalla del asistente En esta pantalla, simplemente pulsaremos en Siguiente, para que se muestre la siguiente pantalla del asistente:

Figura 3, seleccionar el tipo de proyecto Aqu dejaremos la opcin que hay seleccionada, ya que lo que queremos es crear un proyecto de instalacin para una aplicacin de Windows. Pulsamos en Siguiente En la cuarta pantalla del asistente, se nos preguntar qu es lo que queremos incluir en el proyecto de instalacin, tal como se muestra en la Figura 4:

Figura 4, indicar los resultados a incluir en el proyecto de instalacin Si slo queremos distribuir el ejecutable, seleccionaremos la primera de las opciones: Resultado principal del "Nombre del proyecto", en caso de que queramos incluir tambin el cdigo fuente, seleccionaremos la ltima de las opciones que se indican, pero por ahora slo seleccionaremos la primera de ellas. Una vez realizada la seleccin, pulsaremos en Siguiente. Se nos mostrar la siguiente pantalla (ver la Figura 5), desde aqu podemos aadir ms archivos, como por ejemplo imgenes y otros ejecutables, bases de datos, etc., que nuestro programa necesite; en caso de que as sea, pulsaremos en el botn Agregar... y seleccionaremos el o los ficheros extras que queramos. Si no tenemos que aadir nada ms, (o cuando hayamos terminado de agregar los ficheros que queramos), pulsaremos en Siguiente para finalizar el asistente.

Figura 5 Una vez terminado, el asistente nos mostrar informacin sobre las cosas que hemos indicado, tal como se muestra en la Figura 6:

Figura 6 Una vez que hemos terminado con el asistente, (despus de pulsar en el botn Finalizar), tendremos un nuevo proyecto en la solucin del proyecto que queremos distribuir. Esto lo podemos comprobar en el Explorador de soluciones, tal como podemos ver en la figura 7:

Figura 7, el explorador de soluciones

Lo nico que nos queda es generar el proyecto Setup1 y ya tendremos creado el proyecto de instalacin, el cual se encontrar en la carpeta indicada para almacenar ese proyecto de instalacin y dependiendo que estemos en modo Debug o Release, tendremos que navegar hasta la carpeta correspondiente. Dentro de esa carpeta se incluirn estos ficheros:

Figura 8, los ficheros generados con el instalador Realmente slo necesitaremos el fichero con la extensin .msi, ya que el resto es para los casos de que el equipo de destino no disponga del Windows Installer 2.0, si estamos seguro de que es as, slo necesitaremos usar el fichero Setup1.msi, si no estamos seguro de que tenga el Windows Installer 2.0, usaremos los 5 ficheros incluidos en el directorio. Nota: Si seleccionamos Propiedades del proyecto de instalacin (el ltimo icono mostrado al seleccionar el proyecto de instalacin del Explorador de soluciones, ver figura 7), podemos cambiar esta generacin de tantos ficheros "de soporte", es decir, podemos cambiar la configuracin para que slo se genere el fichero con extensin .msi

Proyecto de instalacin sin usar el asistente:

Si te decides por seleccionar un proyecto de instalacin sin usar el asistente, sigue estos pasos para crearlo: 1- Del cuadro de dilogo de Agregar nuevo proyecto (ver figura 1), selecciona Proyecto de instalacin. 2- Se agregar un nuevo proyecto de instalacin. 3- Selecciona el proyecto del Explorador de soluciones, pulsa con el botn secundario del ratn y del men desplegable selecciona la opcin Agregar>Resultados del proyecto. 4- Se mostrar una ventana como la de la figura 9:

Figura 9, agregar grupo de resultados del proyecto 5- Seleccionaremos de la lista desplegable el proyecto que queremos usar y pulsaremos en Aceptar. A partir de este momento, el resto ser como si hubisemos usado el asistente. Tambin podemos usar esta ltima forma de agregar resultados (o ms cosas) en el caso de que queramos distribuir ms de un proyecto, ya que en esa lista desplegable se mostrarn cada uno de los proyectos que tengamos en nuestra solucin (o grupo de proyectos).

Bueno, y esto es todo... espero que, aunque slo haya sido de forma genrica, te haya quedado claro cmo crear un proyecto de instalacin para Windows. En otra ocasin veremos cmo crear otros tipos de proyectos de instalacin y/o cmo configurar o adaptar las imgenes que se mostrarn... aunque esto es lo mismo para un proyecto de Visual Studio .NET como para uno de Visual Studio (o Visual Basic), siempre que usemos el Visual Studio Installer.

Comprobar usuario y clave usando una base de datos Ejemplo para Visual C# 2003 (.NET 1.1)

Introduccin:
En este artculo te muestro cmo verificar si el nombre y la clave de un usuario son correctos, pero comprobando esos datos desde una base de datos de SQL Server. Tambin te dejo un programa que crea la base de datos y la tabla de ejemplo, adems de aadir 4 usuarios de prueba, dos de ellos con las claves guardadas de forma normal y los otros dos en los que las claves se han guardado usando SHA1, para que de esa forma no se guarde en la base de datos el texto "normal". En este artculo, el cdigo de ejemplo es para la versin 2003 de Visual Studio .NET (tanto para Visual Basic como para Visual C#), aunque tambin es vlido para Visual Studio 2005, pero en el caso de Visual Basic, el cdigo de la versin 2005 es algo ms simple.

Qu hace este ejemplo?


Primero hay que entrar en situacin, as que, te explico qu es lo que hace este ejemplo que te voy a mostrar aqu. El ejemplo es similar al publicado anteriormente bajo el ttulo: Iniciar la aplicacin solo al introducir la clave correcta, que como sabrs publiqu en Abril de este ao y del que hay cuatro versiones, segn sea para Visual Studio 2003 o 2005 y en esos dos casos, con el ejemplo para Visual Basic y para C#. No te pongo los cuatro links para no "llenar" demasiado esto, pero pulsando en el link anterior tendrs acceso a las 4 versiones de ese artculo. En este de hoy (del que seguramente tambin habr 4 versiones segn el lenguaje y la versin de Visual Studio/.NET), es algo parecido a aqul, es decir, solo deja iniciar una aplicacin despus de comprobar que el usuario y la clave introducidas son correctos. Pero para saber si la clave introducida es correcta, se busca en una base de datos, en este ejemplo, la base de datos es de SQL Server. Aunque te aviso que el cdigo para una base de datos de Access es muy parecido, solo tienes que cambiar los tipos de objetos usados y la cadena de conexin, as que... si no quieres usar una base de datos de SQL Server, tendrs que "currrtelo" ms... Para que te hagas una idea de qu es lo que hace este cdigo de ejemplo, te muestro una captura de la ventana de comprobacin (ver la figura 1).

Figura 1. El formulario de login (diseo)

No te voy a explicar "los pormenores" de lo que hace la aplicacin de ejemplo, ya que en lo que realmente me quiero concentrar es en explicarte cmo comprobar si el nombre y la clave introducida son correctas. Si quieres saber esos "intrngulis", sigue el link al artculo anterior que te coment antes y as sabrs porqu funciona como funciona. Veamos qu es lo que pasa cuando se pulsa en el botn Aceptar, es decir, cuando se ha escrito el nombre del usuario y la clave y se va a a comprobar si son correctos esos datos.

private void btnAceptar_Click(object sender, System.EventArgs e) {

if( comprobarUsuario(this.txtUsuario.Text, this.txtClave.Text) )


{ this.DialogResult = DialogResult.OK; } else { // Permitir varios intentos veces = veces + 1; if( veces < NumeroIntentos ) { Label1.Text = "Quedan " + (NumeroIntentos - veces) + " intentos"; return; } this.DialogResult = DialogResult.No; } Hide(); }

Como puedes ver en el texto resaltado, lo que hago es llamar a una funcin que es la que se encarga de comprobar si ese nombre de usuario y esa clave son datos correctos. Veamos que hace esa funcin, ya que lo que se hace en ella es la parte importante de este artculo. Primero veamos el cdigo y ahora te explico un poco ms de lo que ya explican los comentarios que es lo que se hace en esa funcin.

private bool comprobarUsuario(string nombre, string clave) { // Conectar a la base de datos SqlConnection cnn = null; // try { // Conectar a la base de datos de SQL Server // (la cadena debe estar inicializada previamente) cnn = new SqlConnection(cadenaCnn); cnn.Open(); // Definir la cadena que vamos a usar para comprobar // si el usuario y el password son correctos. // Utilizo parmetros para evitar inyeccin de cdigo. System.Text.StringBuilder sel = new System.Text.StringBuilder(); // Usando COUNT(*) nos devuelve el total que coincide // con lo indicado en el WHERE, // por tanto, si la clave y el usuario son correctos, // devolver 1, sino, devolver 0

sel.Append("SELECT COUNT(*) FROM Usuarios "); sel.Append("WHERE Nombre = @Nombre AND Clave = @Clave");
// Definir el comando que vamos a ejecutar SqlCommand cmd = new SqlCommand(sel.ToString(), cnn); // Creamos los parmetros cmd.Parameters.Add("@Nombre", SqlDbType.NVarChar, 50); cmd.Parameters.Add("@Clave", SqlDbType.NVarChar, 40); // // Asignamos los valores recibidos como parmetro cmd.Parameters["@Nombre"].Value = nombre; cmd.Parameters["@Clave"].Value = clave;

// // Ejecutamos la consulta // ExecuteScalar devuelve la primera columna de la primera fila // por tanto, devolver el nmero de coincidencias halladas, // que si es 1, quiere decir que el usuario y el password son correctos. int t = Convert.ToInt32(cmd.ExecuteScalar()); // Cerramos la conexin cnn.Close(); // // Si el valor devuelto es cero // es que no es correcto. if( t == 0 ) { return false; } } catch(Exception ex) { MessageBox.Show("ERROR al conectar a la base de datos: \n" + ex.Message, "Comprobar usuario", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); return false; } finally { if( cnn != null ) { cnn.Dispose(); } } // // Si llega aqu es que todo ha ido bien return true; }

Como ya te he comentado, lo que hacemos es comprobar si ese usuario y esa clave son correctas. Tanto el nombre del usuario como la clave, los pasamos como parmetros de la

funcin. Y se buscarn en la base de datos tal y como los pasemos a esa funcin. Para no complicarte porqu te digo esto, sigue leyendo y al final te lo aclaro. Este cdigo "supone" que accedemos a una base de datos que est indicada en la cadena de conexincadenaCnn, y que en esa base de datos hay una tabla llamada Usuarios que al menos tiene dos campos, uno llamado Nombre que es del tipo nvarchar y que tiene una longitud de 50 caracteres y el otro llamado Claveque tambin es del tipo nvarchar y con una longitud de 40 caracteres.

Nota: Por supuesto, para tu caso concreto tendrs que cambiar esos valores por los adecuados, pero eso... debes saber hacerlo t!

Lo que hago es crear una "consulta" (la cadena select, que es el texto resaltado) en la que le digo a la base de datos que cuente cuantos datos hay que coincidan con lo que le indico. Es decir, que compruebe cuantos usuarios hay que tengan el nombre y la clave que se indican. Como es de suponer, no debemos tener ms de un usuario con la misma clave, si ese es el caso... entonces vamos mal... Si ese usuario y esa clave son correctos, esa consulta devolver un uno y si no son correctos, devolver cero. En caso de que devuelva UNO es que es correcto, y si devuelve CERO es que no es correcto. Para saber cuantos datos devuelve esa consulta, uso el mtodo ExecuteScalar del objeto SqlCommand, y tal como est en el comentario del cdigo, (y tambin aqu), ese mtodo devuelve la primera columna de la primera fila de lo indicado en la cadena de seleccin, y como lo que debe devolver esa cadena de seleccin es el nmero de "datos" que coincidan con lo que hay en la parte WHERE, pues resulta que ese valor es en realidad el total de datos, y como te he dicho hace un prrafo, si devuelve CERO es que no existe esa combinacin de nombre/clave, por tanto esos datos no son correctos, por tanto, devolvemos un valor FALSO.

Lo que se comprueba es lo que est en la base de datos


Pues eso, que lo que se comprueba con el cdigo anterior es lo que haya en la base de datos, es decir, en la base de datos el nombre del usuario est tal y como lo indicamos (esto suele ser as), y la clave tambin est como la indicamos, esto ltimo NO DEBERA SER AS, por qu? pues por seguridad, ya que si la clave est en texto "normal", ser ms fcil "averiguarla". Para saber cmo "complicar" un poco la cosa, sigue leyendo.

Guardar los datos de la clave de forma encriptada

Una solucin para que el valor de la clave no est en texto normal, es encriptndola. Yo suelo guardar las claves en formato SHA1 al estilo de como lo hace el propio ASP.NET. Qu consigo con esto? Pues no dejar las claves como texto normal y corriente, sino como una ristra de 40 valores hexadecimales, que no permitan saber que clave es.

Cmo encriptar la clave?


Yo tengo una utilidad para generar la clave SHA1 a partir de una cadena, (en la seccin de WinFX publiqu la versin de esa utilidad para .NET 3.0 y XAML), y lo que hago es convertir la clave en el valor correspondiente de la encriptacin SHA1 y eso es lo que guardo en la base de datos. Si quieres hacer esto mismo con tus claves, el cdigo que te he mostrado antes, al menos el del evento Click del botn, no puedes usarlo as, ya que lo que debes comprobar en la base de datos es el valor SHA1 correspondiente a la clave que hayan escrito en la caja de textos de la clave. Este es el cdigo modificado del evento Click del botn Aceptar, en el que se llama a la funcin que genera el valor SHA1 de la clave introducida.

private void btnAceptar_Click(object sender, System.EventArgs e) { // Convertir a SHA1 la clave introducida

string claveSHA = this.generarClaveSHA1(this.txtClave.Text); if( comprobarUsuario(this.txtUsuario.Text, claveSHA) )


{ this.DialogResult = DialogResult.OK; } else { // Permitir varios intentos veces = veces + 1; if( veces < NumeroIntentos ) { Label1.Text = "Quedan " + (NumeroIntentos - veces) + " intentos"; return; } this.DialogResult = DialogResult.No; } Hide();

Y este es el cdigo de la funcin generarClaveSHA1:


private string generarClaveSHA1(string nombre) { // Crear una clave SHA1 como la generada por // FormsAuthentication.HashPasswordForStoringInConfigFile // Adaptada del ejemplo de la ayuda en la descripcin de SHA1 (Clase) UTF8Encoding enc = new UTF8Encoding(); byte[] data = enc.GetBytes(nombre); byte[] result; SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider(); // This is one implementation of the abstract class SHA1. result = sha.ComputeHash(data); // // Convertir los valores en hexadecimal // cuando tiene una cifra hay que rellenarlo con cero // para que siempre ocupen dos dgitos. StringBuilder sb = new StringBuilder(); for(int i= 0; i< result.Length; i++) { if( result[i] < 16 ) { sb.Append("0"); } sb.Append(result[i].ToString("x")); } // return sb.ToString().ToUpper(); }

Para que ese cdigo te funcione debes tener una importacin al espacio de nombresSystem.Security.Cryptography que es donde se define la clase SHA1CryptoServiceProvider y tambin aSystem.Text que es donde se definen las clases StringBuilder y UTF8Encoding.

Espero que todo lo aqu dicho te sea de utilidad y te sirva para hacer eso que queras saber y que tantas veces me han preguntado...

Un poco ms abajo tienes el cdigo completo de esta utilidad, en la que hay dos formularios para comprobar la clave, uno que no usa la clave SHA1 y otro que si lo usa. Tambin te dejo una utilidad para crear la base de datos de ejemplo y la tabla Usuarios con cuatro valores, dos de ellos usando texto normal y otros dos con las mismas claves, pero guardadas como una ristra de valores que corresponden con la cadena generada por la funcin generarClaveSHA1.

Nos vemos. Guillermo

Los ficheros con el cdigo


La utilidad para crear la base de datos y la tabla de usuarios de ejemplo: crearBaseEjemploClaves_SQL.rar 23.7 KB (MD5: F61A36A495989F0961C12700A21DEE75) El ejemplo completo para comprobar si el usuario y la clave son correctos: formLoginBaseDatos_cs.rar 15.7 KB (MD5: CB7DF77F3BC86C12C06F5504301C6E86)

Espacios de nombres usados en el cdigo de este artculo:


System.Windows.Forms System.Drawing System.Data.SqlClient System.Security.Cryptography System.Text

Iniciar la aplicacin solo al introducir la clave correcta (VB2005) O como iniciar una aplicacin con clave de acceso. Cdigo para Visual Basic 2005 (.NET 2.0)

Introduccin:
Esto es algo que mucha gente pregunta, pero que no tena por aqu publicado, as que ya era hora. El tema es que tenemos una aplicacin de tipo "desktop" (Windows.Forms) en la que necesitamos que el usuario introduzca una clave para poder usarla. El problema con el que se encuentran algunos de los que intentan hacer esto es que escriben el cdigo en el formulario principal y si la clave no es correcta, lo cierran, pero debido a como maneja Visual Studio .NET el tema de los formularios, al cerrar ese formulario principal, se cierra toda la aplicacin... incluso si la clave es correcta! La solucin es crear un formulario para comprobar esa clave, a ese formulario lo llamamos justo al iniciarse la aplicacin, de forma que si la clave introducida es correcta, se muestre el formulario principal, y si no es correcta, cerramos la aplicacin. Debido a que Visual Basic y C# manejan de forma diferente el cdigo insertado automticamente en el formulario, he preferido separar en dos artculos este "truco", as los desarrolladores de C# no tendrn que aguantar las particularidades de Visual Basic .NET y viceversa, adems de que as queda ms claro todo al no tener que explicar cosas diferentes en cada caso, ya que esos dos lenguajes en el fondo van a usar el mismo cdigo (o casi), pero la forma de implementarlo ser diferente. Tambin he puesto independiente el cdigo para las versiones anteriores a Visual Studio 2005 ya que en el caso de Visual Basic 2005 hay que usar otros cuadros de dilogos y con esta separacin tambin quedar ms claro todo. El hacer estas "separaciones" segn el lenguaje y las versiones de esos lenguajes, es porque principalmente los que lean esto sern aquellos que estn empezando a programar, y cuanto ms claro est todo, mejor se aprende.

Aqu te dejo los links a los otros artculos:


Este link es para el cdigo de Visual Basic .NET (2002/2003) (.NET 1.x) Este link es para el cdigo de C# 1.0 (Visual Studio .NET 2002/2003) Este otro es para Visual C# 2005 (.NET 2.0)

Crear el proyecto de ejemplo:

Los pasos que tenemos que dar para crear la aplicacin de ejemplo son los siguientes:

1. Crea un nuevo proyecto del tipo Windows.Forms (Aplicacin para


Windows).

2. Tendremos un formulario llamado Form1, por simplicidad vamos a dejarle


ese mismo nombre (Form1). 3. Aade a ese formulario una etiqueta en la debes escribir lo que quieras, simplemente es para que sepas que es el formulario principal de tu aplicacin. Por supuesto en este formulario es donde tendrs que hacer todo lo que tu aplicacin deba hacer.

4. Aade un segundo formulario al que le dars el nombre FormAcceso,


inicialmente se llamar Form2, pero si le cambias el nombre justo al aadirlo, ser ms cmodo. 5. En ese formulario de acceso, que ser el que usemos para pedir la clave, aade una etiqueta, una caja de textos y dos botones.

6. Modifica la propiedad FormBorderStyle para que tenga el


valor FixedDialog.

7. Asigna un valor False a las


propiedades ControlBox, MaximizeBox y MInimizeBox.

8. A la caja de textos dale el nombre txtClave y si no quieres que se vea la


clave mientras se escribe, asigna un * a la propiedadPasswordChar.

9. A los botones, le asignas los nombres btnAceptar y btnCancelar, y los


textos Aceptar y Cancelar respectivamente. 10. Sita esos controles donde ms te guste, por ejemplo, puede tener un aspecto como el mostrado en la figura 1:

Figura 1. El formulario para la clave de acceso

11. Haz dobleclick en el botn de Cancelar y sustityelo por el siguiente cdigo,


en el que asignamos el valor Cancel al valor devuelto por la propiedad DialogResult que es el valor que devuelve el mtodo ShowDialog de ese formulario (ms abajo vers cmo se usa), en lugar de cerrar el formulario, lo ocultamos para que todo funcione mejor.

Private Sub btnCancelar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles btnCancelar.Click Me.DialogResult = DialogResult.Cancel Hide() End Sub

12. Haz dobleclick en el botn Aceptar y escribe el cdigo de ms abajo, en el


que hacemos la comprobacin de si la clave escrita es la correcta, en este caso 123456, si es as, asignamos un valor OK y si no lo es, asignamos cualquier otro valor, ya que en nuestro cdigo de validacin, daremos como que la clave es correcta si tiene el valor OK. Aqu tambin ocultamos el formulario de las claves con Hide para que contine la ejecucin del programa y vuelva al cdigo desde el que se ha mostrado este formulario.
Private Sub btnAceptar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles btnAceptar.Click If txtClave.Text = "123456" Then Me.DialogResult = DialogResult.OK Else Me.DialogResult = DialogResult.No End If Hide() End Sub

13. En ambos casos, se supone que los nombres de los controles son como te he comentado, si no es as, pues debers cambiar los nombres de los controles, as que, te recomiendo que para este ejemplo uses los nombres que te indico. 14. Una vez hecho esto, ya tenemos el cdigo del formulario que pide la clave de acceso. 15. Tal como est el cdigo solo tendrs un intento para escribir correctamente la clave, despus veremos cmo cambiar el cdigo para que tengas el nmero de intentos que quieras. 16. Ahora vamos a modificar el cdigo del formulario principal, aunque lo que tienes que escribir puede estar en cualquier otro formulario, lo recomendable es que est en el que haga de formulario principal.

17. Aade este cdigo al formulario principal (Form1):


Public Class Form1 Public Shared Sub Main()

Application.EnableVisualStyles() Dim fAcceso As New FormAcceso If fAcceso.ShowDialog() = System.Windows.Forms.DialogResult.OK Then fAcceso.Close() Application.Run(New Form1) End If End Sub End Class

18. Con esto lo que hacemos es cargar el formulario que pide la clave, y si
escribes la clave correcta, se cargar el formulario principal (Form1), si no es correcta, el valor devuelto por ShowDialog ser distinto de OK, por tanto no se carga en memoria el formulario principal y se acabar la aplicacin. Para cargar el formulario principal usamos Application.Run al que le pasamos el formulario que queremos que se utilice como inicio, es decir, ese formulario ser el que mantenga la aplicacin abierta, en cuanto se cierre el formulario principal (Form1) la aplicacin finalizar.

19. Lo siguiente que debemos hacer es decirle al Visual Basic que queremos que
nuestra aplicacin empiece por el mtodo este que acabamos de escribir. Esto en C# no es necesario, ya que siempre existe el mtodo Main y es el que se usa para iniciar la aplicacin.

20. En el men Proyecto, seleccionamos Propiedades... (en realidad te


mostrar Propiedades de y el nombre que le has dado al proyecto), esa misma opcin la puedes conseguir al pulsar con el botn derecho del ratn en el proyecto del Explorador de soluciones, adems puedes pulsar en el icono My Project que est tambin en el Explorador de soluciones. Una vez seleccionadas las opciones, te mostrar una ficha como la de la figura 2. Y tendrs que desmarcar la opcin Habilitar marco de trabajo de la aplicacin, de esa forma, en la lista desplegable que hay debajo de Objeto inicial, podrs seleccionar Sub Main, tal como te muestro en la figura 2:

Figura 2. En las propiedades del proyecto le indicamos que empiece con el cdigo de Sub Main

21. El problema que tenemos ahora es que los estilos de Windows XP ya no


estarn habilitados por defecto, por eso en el cdigo delSub Main que te mostr antes est lo de Application.EnableVisualStyles, para que se usen los estilos de XP con la aplicacin.

22. Y una vez que has pulsado en Aceptar, puede probar a ver si todo funciona
bien, para ello, pulsa en F5 y te mostrar el formulario de acceso, escribe 123456 y pulsa en Aceptar, vers que te muestra el formulario principal (Form1).

23. Si pulsas en Cancelar o escribes mal la clave, se cerrar el programa.

Mejoras al cdigo
Ahora vamos a mejorar un poco el cdigo. Lo primero que vamos a hacer es asignar a los botones del formulario que pide la clave la funcionalidad que normalmente suelen tener, aunque esto lo dejo a tu gusto, esa funcionalidad es que al pulsar Intro sea como si pulsramos en el

botn Aceptar, de esta forma, el usuario escribe la clave y pulsa Intro en tener que buscar el botn y hacer click, aunque tambin puede hacerlo y todo funcionar igual. La otra caracterstica es que el botn Cancelar se asocie con la tecla ESC, de forma que si el usuario pulsa esa tecla sea lo mismo que si hubiera pulsando en Cancelar. Para conseguir esto, debes hacer lo siguiente: 1. Muestra el formulario de la clave de acceso y pulsa una vez en cualquier parte del formulario, pero no en ninguno de los controles.

2. Ahora en la ventana de propiedades (si no la ves, pulsa F4), selecciona la


propiedad AccepButton y de la lista desplegable (ver la figura 3) selecciona btnAceptar.

Figura 3. Asignar el botn predeterminado

3. Una vez hecho eso, vers que el botn Aceptar tiene un borde ms oscuro,
eso indica que es el botn predeterminado, por tanto ser el que reciba la pulsacin de la tecla Intro.

4. Ahora selecciona la propiedad CancelButton y de la lista


selecciona btnCancelar.

5. Ya tienes esa funcionalidad, que puedes probar si inicias la aplicacin (F5) y


despus de escribir la clave pulsas INTRO, vers que no tienes que hacer click en el botn Aceptar.

6. Lo mismo ocurre si pulsas la tecla ESC, vers que se cierra la aplicacin


aunque la clave sea correcta, ya que en realidad es como si hubieras pulsado en el botn Cancelar. Sigamos con las mejoras:

Permitir varios intentos fallidos antes de cerrar la aplicacin


Como es posible que el usuario se equivoque al escribir o se olvide de la clave, vamos a darle algunas oportunidades ms, por ejemplo tres. Para hacer esto, tenemos que modificar el cdigo del formulario de acceso, por tanto muestra el panel del cdigo de ese formulario (FormAcceso) y antes del cdigo que aadimos al principio, escribe esto:
Private veces As Integer = 0 Private Const NumeroIntentos As Integer = 3

La constante NumeroIntentos tendr el valor de las veces que vamos a permitir que escriba la clave antes de darlo como cosa perdida y cerrar la aplicacin. Ahora escribe esto en el mtodo del botn Aceptar, de forma que esto sea el nuevo cdigo:
Private Sub btnAceptar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles btnAceptar.Click If txtClave.Text = "123456" Then Me.DialogResult = DialogResult.OK Else ' Permitir varios intentos veces = veces + 1 If veces < NumeroIntentos Then Label1.Text = "Quedan " & (NumeroIntentos - veces) & " intentos" Exit Sub End If Me.DialogResult = DialogResult.No End If Hide() End Sub

Lo que hacemos es incrementar el contenido de la variable veces cada vez que se pulse en Aceptar, pero cuando la clave que escribimos no es la correcta, ya que si es correcta no hay que dar ms intentos, je, je. Si el valor de esa variable en menor que el nmero mximo de intentos, modificamos el contenido de la etiqueta que hay junto a la caja de textos (Label1) para que muestre los intentos que

quedan. Si superamos ese nmero se volver al otro cdigo y se acabar la aplicacin.

Y esto es todo, espero que te haya resultado sencillo de comprender, y si ya tenas ms experiencia, espero que sepas perdonar que lo haya explicado tan "paso a paso". Que lo disfrutes!

Averiguar la cultura actual para ajustar las fechas correctamente

Y cmo hacer una consulta en SQL Server usando parmetros

Introduccin:
Es una cosa muy simple, pero creo que es conveniente saber cmo hacerlo. Te explico que este cdigo es el que utilizo en mis foros a la hora de mostrar la fecha actual. Debido a que el foro est alojado en un servidor que reside en los Estados Unidos de Norteamrica (USA), dicho formato de fecha y hora es diferente, por ejemplo al usado en Espaa. En realidad, la fecha la puedo mostrar como quiera, pero si, por ejemplo, esa fecha (o formato) la quiero usar para acceder a una base de datos, si no est en el mismo formato del idioma en el que se ejecuta la base de datos, posiblemente no funcionar correctamente. El cdigo que utilizo es el siguiente, en este caso para mostrar dos fechas, una 15 das antes de la actual y otra la actual, ambas en formato universal, es decir, segn la hora universal (UTC) o de Greenwich (GMT):

Dim s As String s = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePatt ern txtDesde.Text = DateTime.Now.ToUniversalTime().Subtract(New TimeSpan(15, 0, 0, 0)).ToString(s) txtHasta.Text = DateTime.Now.ToUniversalTime().ToString(s)

En este caso, estoy usando la propiedad ShortDatePattern que vendra a ser algo as como MM/dd/yyyy, es deir, en formato mes/da/ao. Por otro lado, al usar el mtodo ToUniversalTime de la la propiedad Now, estoy obteniendo la fecha UTC, es decir, para que no utilice la del pacfico, que es la hora que tiene mi servidor, por ejemplo, estos valores que se muestran a continuacin se obtienen directamente del servidor, y en el caso de que ests viendo la pgina desde elGuille.info, (pulsa en el link para ir a esta misma pgina en elguille.info), vers que hay unas 8 horas de diferencia con la fecha universal (UTC/GMT), sin embargo, si lo ves desde MundoProgramacion.com, (pulsa en el link para ir a esta misma pgina en mundoprogramacion), esa fecha ser la de Espaa, ya que el servidor donde est alojado ese sitio reside en Espaa. La fecha en formato "normal", usando dd/MM/yyyy: 08/09/2011 Cdigo usado: Dim fecha1 As String = DateTime.Now.ToString("dd/MM/yyyy") Este mostrando la fecha y la hora con dd/MM/yyyy HH:mm: 08/09/2011 23:37 La fecha en formato "universal", usando dd/MM/yyyy: 08/09/2011 Cdigo usado: Dim fecha2 As String =

DateTime.Now.ToUniversalTime().ToString("dd/MM/yyyy") Este mostrando la fecha y la hora con dd/MM/yyyy HH:mm: 08/09/2011 21:37 La fecha segn la cultura (en formato universal): 9/8/2011 Cdigo usado: Dim fecha3 As String = DateTime.Now.ToString(s) La fecha segn la cultura (segn la hora del servidor): 9/8/2011 Cdigo usado: Dim fecha4 As String = DateTime.Now.ToUniversalTime().ToString(s) En estos dos ltimos casos, el valor de "s" se obtiene con: s= System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePatter n

Nota: Internamente esta pgina est usando cdigo de Visual Basic .NET para mostrar esas fechas.

Cmo influye esto en una consulta de SQL Server?


Aunque parezca una tontera, al usar los valores desde y hasta del cdigo mostrado ms arriba, si esos valores los uso en una consulta de SQL Server para obtener los datos entre dos fechas, funcionar bien lo ejecute donde lo ejecute, pero si en lugar de usar la "fecha globalizada" usara el formato habitual: dd/MM/yyyy, slo me funcionara si la base de datos estuviese en un servidor configurado con ese "tipo" de fecha. La consulta usada es esta: (en realidad faltan cosas que indicar, pero lo importante es cmo usarla)
''' <summary> ''' Obtener los registros de una tabla entre dos fechas. ''' </summary> ''' <param name="desde">Fecha desde la que queremos los datos</param> ''' <param name="hasta">Fecha hasta la que queremos los datos</param> ''' <param name="max">El mximo de mensajes a devolver (entre 100 y 600, por defecto 400)</param> ''' <returns>Un DataTable con los mensajes hallados</returns> ''' <remarks></remarks> ''' <fecha>29/Dic/2005</fecha> Public Shared Function MensajesForo( _ ByVal desde As String, _ ByVal hasta As String, _

Optional ByVal max As Integer = 400) As DataTable ' Dim cnn As SqlConnection = Nothing Dim dt As New DataTable ' ' Dentro de un Try/Catch por si se produce un error Try If max > 600 OrElse max < 100 Then max = 400 ' Obtenemos la cadena de conexin adecuada Dim sConn As String = "data source=(local); " & _ "initial catalog=<nombre base de datos>; " & _ "user id=<usuario>; password=<contrasea>;" cnn = New SqlConnection(sConn) cnn.Open() ' Creamos el comando para la consulta Dim cmd As SqlCommand = New SqlCommand Dim sel As String = _ "SELECT TOP " & max & " <los campos a devolver> " & _ "FROM <nombre de la tabla> " & _ "WHERE ( Fecha >= @Fecha1 AND Fecha <= @Fecha2 ) " & _ "ORDER BY Fecha DESC" ' cmd.CommandText = sel cmd.Connection = cnn cmd.CommandType = CommandType.Text cmd.CommandTimeout = 90 ' Los parmetros usados en la cadena de la consulta cmd.Parameters.Add(New SqlParameter("@Fecha1", SqlDbType.DateTime)) cmd.Parameters.Add(New SqlParameter("@Fecha2", SqlDbType.DateTime)) cmd.Parameters("@Fecha1").Value = desde cmd.Parameters("@Fecha2").Value = hasta ' ' Creamos el dataAdapter y asignamos el comando de seleccin Dim da As New SqlDataAdapter da.SelectCommand = cmd ' Llenamos la tabla da.Fill(dt) Catch 'ex As Exception ' Si hay error, devolvemos un valor nulo. Return Nothing Finally

' Por si se produce un error, ' comprobamos si en realidad el objeto Connection est iniciado, ' de ser as, lo cerramos. If Not cnn Is Nothing Then cnn.Close() End If End Try ' ' Devolvemos el objeto DataTable con los datos de la consulta Return dt End Function

Y esto es todo, espero que te sea de utilidad. Nos vemos. Guillermo

El cdigo para VB .NET


Es el mostrado anteriormente.

El cdigo para C#
Este es el cdigo para asignar las cadenas de las fechas segn la cultura actual:
string s; s = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePatt ern; string desde = DateTime.Now.ToUniversalTime().Subtract(new TimeSpan(15, 0, 0, 0)).ToString(s); string hasta = DateTime.Now.ToUniversalTime().ToString(s);

Este es el cdigo para acceder a la base de datos y realizar la consulta:

/// <summary> /// Obtener los registros de una tabla entre dos fechas. /// </summary> /// <param name="desde">Fecha desde la que queremos los datos</param> /// <param name="hasta">Fecha hasta la que queremos los datos</param> /// <param name="max">El mximo de mensajes a devolver (entre 100 y 600, por defecto 400)</param> /// <returns>Un DataTable con los mensajes hallados</returns> /// <remarks></remarks> /// <fecha>29/Dic/2005</fecha> public static DataTable MensajesForo(string desde, string hasta, int max) { // SqlConnection cnn = null; DataTable dt = new DataTable(); // // Dentro de un Try/Catch por si se produce un error try { if( max > 600 || max < 100 ) max = 400; string usuario = "<usuario>"; string passw = "<password>"; // Obtenemos la cadena de conexin adecuada string sConn = "data source=(local); " + "initial catalog=<nombre base de datos>; " + "user id=" + usuario + "; password=" + passw + ";"; cnn = new SqlConnection(sConn); cnn.Open(); // Creamos el comando para la consulta SqlCommand cmd = new SqlCommand(); string sel = "SELECT TOP " + max + " <los campos a mostrar> " + "FROM <nombre de la tabla> " + "WHERE ( Fecha >= @Fecha1 AND Fecha <= @Fecha2 ) " + "ORDER BY Fecha DESC"; // cmd.CommandText = sel; cmd.Connection = cnn; cmd.CommandType = CommandType.Text;

cmd.CommandTimeout = 90; // Los parmetros usados en la cadena de la consulta cmd.Parameters.Add(new SqlParameter("@Fecha1", SqlDbType.DateTime)); cmd.Parameters.Add(new SqlParameter("@Fecha2", SqlDbType.DateTime)); cmd.Parameters["@Fecha1"].Value = desde; cmd.Parameters["@Fecha2"].Value = hasta; // // Creamos el dataAdapter y asignamos el comando de seleccin SqlDataAdapter da = new SqlDataAdapter(); da.SelectCommand = cmd; // Llenamos la tabla da.Fill(dt); } catch(Exception ex) { // Si hay error, devolvemos un valor nulo. Console.WriteLine(ex.Message); return null; } finally { // Por si se produce un error, // comprobamos si en realidad el objeto Connection est iniciado, // de ser as, lo cerramos. if( cnn != null ) cnn.Close(); } // // Devolvemos el objeto DataTable con los datos de la consulta return dt; }

Detectar la pulsacin de teclas y cambiar el cdigo de la tecla pulsada y otras cosillas

Si has usado cualquier versin de Visual Basic anterior a .NET, seguramente sabrs cmo saber que tecla se ha pulsado en un textbox (u otro control), interceptando los eventos KeyPress o KeyDown/KeyUp. En el primer evento, ese cdigo estaba indicado por el parmetro KeyAscii, en los otros dos, el cdigo lo indicaba el parmetro KeyCode. En el caso del evento KeyPress, podamos saber si se haba pulsado la tecla Intro con una comparacin como esta: If KeyAscii = vbKeyReturn Then Y a continuacin, (para evitar el pitido), asignbamos un valor cero a KeyAscii: KeyAscii = 0 Pero con el .NET Framework, (no slo con VB, tambin con C# u otro lenguaje), esto no es posible... al menos de la misma forma que se haca con el Visual Basic clsico. En el caso del evento KeyPress, para saber qu tecla se ha pulsado, debemos usar e.KeyChar, (que sera el equivalente al parmetro KeyAscii en VB clsico), pero la propiedad KeyChar es de solo lectura, por tanto no se puede asignar un valor a dicha propiedad... aunque esto en teora no es problema, ya que te mostrar la solucin. El hecho de asignar un valor cero a KeyAscii es para indicar que no se debe tener en cuenta la pulsacin de dicha tecla, esto en .NET se hace asignando un valor verdadero a la propiedad Handled del objeto e: e.Handled = True Con esto indicamos que esa tecla ha sido "manejada" por nuestro evento y se ignorar. Pero en otras ocasiones, lo que nos interesa es cambiar la tecla pulsada por otra diferente. Por ejemplo, si en nuestra configuracin regional tenemos que los decimales se representan por la coma, podamos querer que al pulsar en el punto, sta pulsacin se convierta en una coma. Nuevamente en Visual Basic clsico era tan simple como esto: If KeyAscii = Asc(".") Then KeyAscii = Asc(",") Es decir, si la tecla es el punto, cambiarla por la coma. Pero como acabamos de ver, en .NET no se puede asignar un nuevo valor a la propiedad KeyChar, pero para solucionarlo, podemos echar mano de la clase SendKeys (a diferencia del VB clsico, ahora no es una funcin de VB sino una clase de .NET Framework) y lanzar la pulsacin de la tecla que nos interese: SendKeys.Send(",") Pero si hacemos slo esto, nos encontramos con dos pulsaciones: el punto que el usuario ha pulsado y la coma que nosotros hemos "enviado" al teclado. La solucin es la misma que acabamos de ver anteriormente: indicar que el usuario no ha pulsado nada: e.Handled = True, con esto slo quedar la tecla que nosotros hemos enviado con SendKeys.

A continuacin te muestro el cdigo de un formulario de prueba, tanto para VB como para C#, en el que adems de ver esto de la pulsacin y cmo saber que tecla se ha pulsado (y cambiar el cdigo de la tecla pulsada), tambin veremos cmo detectar la pulsacin de la tecla suprimir en un ListBox y saber cmo averiguar la tecla pulsada, ya que el tipo de datos de KeyChar es del tipo Char y en .NET esto de convertir teclas normales y Char es un poco "rollo". Adems vers cmo seleccionar todo el texto de un textbox cuando el control toma el foco.

Cmo saber el cdigo de la tecla pulsada:

En Visual Basic es relativamente fcil, en C# an lo es ms, si usamos los cdigos de "escape". Veamos primero cmo hacerlo con VB: Podemos saber qu tecla se ha pulsado usando tanto con la funcin ChrW como con el mtodo ToChar de la clase Convert, adems de usar la conversin de una cadena a char usando la letra C despus de la cadena, aunque esto slo se puede usar con teclas normales (las que podemos representar desde el teclado): If e.KeyChar = Convert.ToChar(Keys.Return) Then If e.KeyChar = ChrW(Keys.Return) Then If e.KeyChar = "."c Then

En C# podemos hacerlo de estas dos formas: if( e.KeyChar == '\r' ) if( e.KeyChar == Convert.ToChar('\r') )

Aqu tienes el cdigo para los dos ejemplos (en VB y en C#): pulsarTeclas.zip 14.3 KB

Saber que tecla se ha pulsado en un textbox y otras cosillas (VB)


Este es el cdigo para Visual Basic .NET Private Sub txtNumero_KeyPress(ByVal sender As Object, _ ByVal e As System.Windows.Forms.KeyPressEventArgs) _ Handles txtNumero.KeyPress ' Si se pulsa la tecla Intro, pasar al siguiente If e.KeyChar = Convert.ToChar(Keys.Return) Then 'If e.KeyChar = ChrW(Keys.Return) Then e.Handled = True txtFecha.Focus()

ElseIf e.KeyChar = "."c Then ' si se pulsa en el punto se convertir en coma e.Handled = True SendKeys.Send(",") End If End Sub ' Private Sub txtFecha_KeyPress(ByVal sender As Object, _ ByVal e As System.Windows.Forms.KeyPressEventArgs) _ Handles txtFecha.KeyPress Select Case e.KeyChar Case Convert.ToChar(Keys.Return) 'ChrW(Keys.Return) e.Handled = True btnAdd.Focus() Case "."c, ","c, "-"c ' si se pulsa en estos caracteres, se convertir en / e.Handled = True SendKeys.Send("/") End Select End Sub ' Private Sub txtNumero_Enter(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles txtNumero.Enter, txtFecha.Enter ' Al recibir el foco, seleccionarlo todo CType(sender, TextBox).SelectAll() End Sub ' Private Sub ListBox1_KeyDown(ByVal sender As Object, _ ByVal e As System.Windows.Forms.KeyEventArgs) _ Handles ListBox1.KeyDown ' Si se pulsa en Supr, borrar el elemento seleccionado If e.KeyCode = Keys.Delete Then ListBox1.Items.Remove(ListBox1.SelectedItem) End If End Sub ' Private Sub btnAdd_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles btnAdd.Click ' Aadir el contenido de las cajas al listbox ListBox1.Items.Add(txtNumero.Text & ", " & txtFecha.Text) txtNumero.Focus() End Sub ' Private Sub btnCerrar_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles btnCerrar.Click ' cerrar el formulario ' (si este es el nico formulario, tambin se cerrar el ejecutable) Me.Close() End Sub

Saber que tecla se ha pulsado en un textbox y otras cosillas (C#)


Este es el cdigo para C#

private void txtNumero_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { // Si se pulsa la tecla Intro, pasar al siguiente //if( e.KeyChar == Convert.ToChar('\r') ){ if( e.KeyChar == '\r' ){ e.Handled = true; txtFecha.Focus(); }else if( e.KeyChar == '.' ){ // si se pulsa en el punto se convertir en coma e.Handled = true; SendKeys.Send(","); } } private void txtFecha_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { // switch(e.KeyChar){ case '\r': e.Handled = true; btnAdd.Focus(); break; case '.': case ',': case '-': // si se pulsa en estos caracteres, se convertir en / e.Handled = true; SendKeys.Send("/"); break; } } private void btnAdd_Click(object sender, System.EventArgs e) { // Aadir el contenido de las cajas al listbox ListBox1.Items.Add(txtNumero.Text + ", " + txtFecha.Text); txtNumero.Focus(); } private void btnCerrar_Click(object sender, System.EventArgs e) { // cerrar el formulario // (si este es el nico formulario, tambin se cerrar el ejecutable) this.Close(); } private void ListBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) { // Si se pulsa en Supr, borrar el elemento seleccionado if( e.KeyCode == Keys.Delete ) { ListBox1.Items.Remove(ListBox1.SelectedItem); } } private void txtNumero_Enter(object sender, System.EventArgs e) {

// Al recibir el foco, seleccionarlo todo ((TextBox)sender).SelectAll();

Manejar datos de configuracin

Una clase para leer y guardar datos de una aplicacin

Introduccin
Esta clase que te muestro en este artculo es la que me fabriqu para guardar y leer los datos de configuracin de mis aplicaciones. En realidad empec a usarla con Visual Basic 2005, y de hecho el cdigo usado para generarla es en ese lenguaje, y por eso est en esta seccin de "trucos de .NET 2.0", pero con pequeos cambios la puedes adaptar al Visual Basic 2003 e incluso al 2002, cuando te indique las "cosillas" que puedes hacer con esta clase, te indicar que cambios debes hacer. Para que puedas probar esta clase, adjunto una aplicacin de prueba, que est hecha con el Visual Basic 2005 usando el Visual Studio 2005 Team Edition para Software Developers, y aunque no la he probado en el Visual Basic 2005 Express Edition, estoy seguro de que funciona, ya que no utiliza nada especial que no se instale con la versin gratuita de Visual Basic 2005.

Pulsa aqu, si quieres ver un ejemplo anterior para Visual Studio 2003 (VB y C#) Con fecha del 17/Ago/06 he publicado la versin para Visual Studio .NET (2003) Actualizado 02/May/06: Pulsa aqu para ver esta misma clase para usarla con C# 2005 Actualizado 12/Ene/2008: He publicado una actualizacin del cdigo para Visual Basic 2005/2008

Funcionalidad de la clase ConfigXml


Como te puedes imaginar por el ttulo de esta seccin, la clase se llama ConfigXml, Config porque sirve para guardar datos de configuracin y Xml porque ese es el formato que utiliza, de hecho utilizo instrucciones Xml para leer los nodos del fichero de configuracin lo mismo que para guardarlos. Pero eso ya lo descubrirs en el cdigo fuente, ahora veamos que mtodos y propiedades tiene la clase: Propiedad o mtodo GuardarAlAsignar Descripcin True para guardar automticamente los valores al asignarlos El nombre del fichero de configuracin

FileName

Leer los valores GetValue(ByVal seccion As String, ByVal clave As String) As String GetValue(ByVal seccion As String, ByVal clave As String, ByVal predeterminado As String) As String GetValue(ByVal seccion As String, Valor devuelto como entero ByVal clave As String, ByVal predeterminado As Integer) As Integer GetValue(ByVal seccion As String, ByVal clave As String, ByVal predeterminado As Boolean) As Boolean Guardar los valores como elementos SetValue(ByVal seccion As String, ByVal clave As String, ByVal valor As String) SetValue(ByVal seccion As String, ByVal clave As String, ByVal valor As Integer) SetValue(ByVal seccion As String, ByVal clave As String, ByVal valor As Boolean) Guardar los valores como un par key/value SetKeyValue(ByVal seccion As String, ByVal clave As String, ByVal valor As String) SetKeyValue(ByVal seccion As String, ByVal clave As String, ByVal valor As Integer) SetKeyValue(ByVal seccion As String, ByVal clave As String, ByVal valor As Boolean) Guardar un valor en la seccin y clave indicadas. Guardar un valor entero Valor devuelto como Boolean Leer un dato de la seccin y clave indicadas.

Guardar un valor Boolean, internamente se guarda como cadena, siendo "0" = False, y "1" = True

RemoveSection(ByVal seccion As String) Save() Read() Mtodos exclusivos para .NET 2.0 Secciones() As List(Of String)

Eliminar la seccin indicada Guardar fsicamente los datos Leer los datos

Devuelve todas las secciones

Claves(ByVal seccion As String) As Dictionary(Of String, String) Constructores New(ByVal fic As String) New(ByVal fic As String, ByVal guardarAlAsignar As Boolean)

Devuelve los valores y claves de la seccin indicada.

Adivina para que son los parmetros de los dos constructores

Como te indico, hay dos mtodos que solo funcionarn en .NET 2.0, ya que devuelven dos tipos generic, el primero de tipoList(Of String) que lo puedes sustituir por un ArrayList. El otro devuelve una coleccin de tipo Dictionary(Of String, String) que puedes sustituir por una coleccin de tipo Hashtable.

Aqu tienes el cdigo completo de la clase para Visual Basic 2005

La aplicacin de ejemplo
En el formulario de ejemplo te muestro cmo usar los mtodos SetValue y RemoveSection, adems de las dos colecciones que leen las secciones y el contenido de una seccin, ya que en ese formulario de ejemplo hay un ListView en el que se muestra el contenido del fichero de configuracin. El aspecto del formulario en tiempo de diseo es este:

Figura 1. El formulario de ejemplo en tiempo de diseo

Aqu tienes el cdigo del formulario para Visual Basic 2005

Espero que te sea de utilidad, que de eso es de lo que se trata. Nos vemos. Guillermo Nerja, 21 de Febrero de 2006

Cdigo de la clase:

Nota: El cdigo de la clase ConfigXml est en la pgina en la ltima actualizacin.

Cdigo del formulario:

'----------------------------------------------------------------------------' Prueba de la clase ConfigXml (21/Feb/06) ' para guardar datos de configuracin en formato Xml ' ' Guillermo 'guille' Som, 2006 '----------------------------------------------------------------------------Option Strict On Public Class Form1 Private mCfg As elGuille.info.Util.ConfigXml Private Sub btnLeer_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnLeer.Click ' Leer el fichero

Dim openFD As New OpenFileDialog openFD.Title = "Selecciona el fichero de configuracin" openFD.Filter = "Configuracin|*.cfg;*.config;*.configuration|Todos los ficheros|*.*" openFD.FileName = txtFic.Text openFD.Multiselect = False openFD.CheckFileExists = False If openFD.ShowDialog = Windows.Forms.DialogResult.OK Then mCfg = New elGuille.info.Util.ConfigXml(openFD.FileName, True) actualizarListView() Dim b As Boolean = True Me.btnAdd.Enabled = b Me.btnEliminarSec.Enabled = b Me.btnGuardar.Enabled = b End If End Sub Private Sub actualizarListView() ListView1.Items.Clear() For Each s As String In mCfg.Secciones For Each s1 As KeyValuePair(Of String, String) In mCfg.Claves(s) Dim lvi As ListViewItem = ListView1.Items.Add(s) lvi.SubItems.Add(s1.Key) lvi.SubItems.Add(s1.Value) Next Next End Sub Private Sub btnEliminarSec_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnEliminarSec.Click If MessageBox.Show("Quieres eliminar la seccin " & txtSec.Text & "?", _ "Eliminar seccin", _ MessageBoxButtons.YesNo, _ MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then mCfg.RemoveSection(txtSec.Text) actualizarListView() End If End Sub

Private Sub btnAdd_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnAdd.Click ' Aadirlo / actualizarlo y rellenar el ListView If mCfg Is Nothing Then MessageBox.Show("Debes indicar el ficehero en el que guardar los datos", _ "Aadir", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Exit Sub End If mCfg.SetValue(txtSec.Text, txtClave.Text, txtValor.Text) actualizarListView() End Sub Private Sub ListView1_SelectedIndexChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles ListView1.SelectedIndexChanged Dim lvi As ListViewItem If ListView1.SelectedItems.Count > 0 Then lvi = ListView1.SelectedItems(0) Me.txtSec.Text = lvi.SubItems(0).Text Me.txtClave.Text = lvi.SubItems(1).Text Me.txtValor.Text = lvi.SubItems(2).Text End If End Sub Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' Deshabilitar los botones y controles ' hasta que se indique el fichero Dim b As Boolean = False Me.btnAdd.Enabled = b Me.btnEliminarSec.Enabled = b Me.btnGuardar.Enabled = b txtFic.Text = System.IO.Directory.GetCurrentDirectory() & "\prueba.cfg" End Sub Private Sub btnGuardar_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnGuardar.Click

' No hace falta si se indica "guardar automticamente" en el constructor mCfg.Save() End Sub End Class

Potrebbero piacerti anche