22 dic 2009

Modo Dios en Windows 7

Que tiempos aquellos del DOOM donde si nos frustrábamos mucho tratando de pasar un nivel o un boss podíamos activar el modo dios y nos volvíamos invulnerables, invencibles y entonces nos desquitábamos de todo lo sufrido.

Pues resulta y acontece que el recientemente lanzado Windows 7 tiene su modo dios. Consiste básicamente en un truquillo para activar un super panel de control donde existen muchas más opciones de configuración que en el panel normal.

¿Cúal es es el truco? Creamos una carpeta y la nombramos asi: "GodMode.{ED7BA470-8E54-465E-825C-99712043E01C}" sin las comillas. La carperta cambia de icono y queda nombrada como GodMode y como ya dijimos al explorarla lo que nos aparece es una gran gama de opciones. Yo la probé en un Windows 7 Profesional de 32 bits y la lista llega a alrededor de 275 opciones (La mayoría de accesibilidad).

La verdad no es la gran cosa, no nos volvemos ni invulnerables ni invencibles, pero es un truquillo interesante para apantallar a los amigos y ademas quién sabe sin en el futuro alguna de esas opciones extra nos saque de algún apuro.

8 dic 2009

Datos válidos se cargan nulos desde archivos Excel

En el post OLEDB Para cagar Archivos de Excel habíamos indicado una manera de cargar datos desde un archivo Excel utilizando OleDB explicando algunas de las propiedades del string de conexión según lo entendí en ese momento.

Ahora se nos presentó un problema menor con esta aproximación: Resulta que tenemos una columna cuyas celdas traen valores que normalmente son alfanuméricos, por lo que las celdas se formatean como texto; sin embargo en un punto del proceso se modificó manualmente un par de celdas de dicha columna con valores numéricos.

A la hora de realizar la consulta las dos celdas modificadas con valores numéricos son retornadas como nulas, específicamente con el valor DBNull de .Net. Estuvimos investigando y acontece que ése es el comportamiento normal, osea así es como funciona.

Cuando utilizamos este modo de cargar datos el tipo de la columna que prevalece es el de la mayoría de las celdas. Para clarificar, supongamos que tenemos una columna con 8 celdas:
  • Si 5 celdas son texto y 3 son numéricas. la consulta devolverá 5 valores (los texto) y 3 nulos.

  • Si 5 celdas son numericas y 3 son texto la consulta devolverá 5 valores (los numéricos) y 3 nulos.

  • Si 4 son numéricas y 4 son texto la consulta devolverá 4 valores (los numéricos) y 4 nulos.

Ahora bien, hay una forma de tratar con este problema: utilizando el valor IMEX de las Extented Properties del string de conexión. IMEX se utiliza precisamente para indicar que se importaran valores de tipos mixtos, pero lo hace convirtiendo todo a texto. Como lo mencionamos en el artículo mencionado anteriormente, utilizar el IMEX=1 "significa que se tomaran los valor tal y como se FORMATEAN en las celdas" por lo que hay que valorar y sopesar el uso de esta propiedad.

OleDbConnection cnnOle = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=C:Pruebas\test.xls;
Extended Properties=\"EXCEL 8.0;HDR=NO;IMEX=1\";");
cnnOle.Open();

En nuestra situación particular optamos por instruir al usuario del aplicativo que en caso de requerir modificar valores de la columna en cuestión se debe asegurar que las celdas se formatearan como texto, lo cual puede hacerse de manera simple: antes incluir cualquier dato utilizar una comilla simple. Ademas añadimos alertas durante la carga de que los valores nulos no se cargaran.

Algunos links que tratan sobre este asunto:

http://support.microsoft.com/kb/257819/
http://support.microsoft.com/kb/194124/EN-US/

20 nov 2009

Crear scopes personalizados en MOSS

Continuando con el ultimo Post Configurar la búsqueda en MOSS ahora podemos realizar scopes personalizadas para realizar búsquedas especificas.

Por ejemplo si quisiéramos definir un scope para buscar en el sitio únicamente documentos de Word 2007 (Archivos .docx) realizaríamos los siguientes pasos:

1- Entramos al sitio del Share Service “SharedServices1” y ubicamos la opción Search->Sarch Settings->Scopes->View Scopes y desde aquí creamos un nuevo “scope”:

Ya podríamos añadirle reglas al scope que son las que definen los alcances de las búsquedas, como por ejemplo que busque únicamente en tal dirección o documentos creados por tal usuario. Sin embargo si queremos usar búsquedas de manera que solo encuentre documentos de Word necesitamos hacer algo más primero.

2- Una vez creado el scope necesitamos crear una regla, pero para hacerlo adecuadamente de manera que podemos utilizar propiedades como el FileExtension necesitamos habilitarla para usar en scopes. Para esto vamos al sitio del Share Service y ubicamos la opción Search->Sarch Settings->Crawl Settings -> Metadata property mappings.

Cuando elegimos esta opción se nos muestra lista con todas las propiedades que se encuentran disponibles sin embargo estas no se encuentran habilitadas para usar en los scopes. Para lograr esto elegimos la que deseamos habilitar, en este ejemplo, FileExtension y checkeamos la casilla que dice “Allow this property to be used in scopes”y OK.


3 – Ahora ya podemos crear la regla que necesitamos en el scope. Ubicamos el scope que habíamos creado “Documentos word” y le indicamos “New Rule” para crear a nueva regla.


Una vez en la pantalla que creamos las reglas la configuramos de manera que en el espacio “Scope Rule Type” nos quede seleccionado “Property Query” , En la sección Property Query nos qu de seleccionada la propiedad FileExtension y en el valor igual la palabra “docx”, finalmente debemos indicarle que el Behavior (comportamiento) es requerido.

De esta manera hemos creado un scope con una regla que nos indica que solo buscara documentos docx osea documentos de Word 2007.

5- Importante no olvidar actualizar el scope para que aplique la regla que acabos de crear. Para esto en el sitio del Share Service “SharedServices1” ubicamos la opción Search->Sarch Settings->Scopes->Start update now


4 -Ahora necesitamos que este scope aparezca en el combo que se encuentra en la pantalla de búsqueda de nuestro sitio. Para esto vamos directamente sitio en el que necesitamos aparezca el scope (en nuestro caso el sitio documental) y le damos sobre Site Actions->Site Settings.


Una vez en la pantalla de settings ubicamos la opción Site Collection Administration -> Search Scope

En esta opción se nos muestran los Display Groups, como deberíamos poder apreciar el scope que creamos ,“Documentos Word”, esta en la sección “Unused Scopes” lo que necesitamos hacer es que quede en el grupo “Search Dropdown”, para eso damos clic sobre este ultimo. Nos saldrá entonces para seleccionar los scopes disponibles, Marcamos el que queremos y elegimos en que posición deseamos que aparezca (si hubiese más de un scope) y le damos clic en ok


Y ya con esto nos debería poder aparecer en los combos de búsqueda el scope “Documentos Word”.

21 oct 2009

Configurar la búsqueda en MOSS

Últimamente bajo un poco el stress habitual de trabajo por lo que tuvimos oportunidad de hacer algo de R&D de SharePoint Server. He aquí la forma que encontramos de configurar la búsqueda.

Suponemos que ya tenemos un sitio Documental, testDoc. Ahora necesitamos habilitar la búsqueda en el mismo. Para esto seguimos los siguientes pasos:

1 – Lo primero es crear un Share Service, para esto desde el ‘Sitio de Administración Central” ubicamos la opción Application Management -> Office SharePoint Server Shared Services ->Create or Configured this farm’s shared services


Desde aquí hay escoger “New SSP ” y configurar el Share Service de manera que el web aplication corresponda al sitio en el que queremos buscar.

2- Seguidamente hay que habilitar el sesión state: Application Manager-> Office SharePoint Server Shared Services-> Configure sesión state


3 – Configurar el Source del Shared Service recién creado. Para esto se necesita ir al sitio del Shared Service, en nuestro ejemplo es ”SharedServices1” dentro del sitio ir a Search->Sarch Settings->Crawl Settings ->Content sources and crawl schedules.


Dentro de esta pantalla configuramos el Content source que esta creado por default, de manera que en “Start Addresses” se encuentre la raíz del sitio en el que queremos que se realice la búsqueda y le decimos que realice “Start full crawl of this content source” (es el último checkbox de la página) para que realice de una vez el rastreo en el sitio


Ya con esto debería bastar para que la búsqueda funcione en nuestro sitio documental. Sin embargo podemos crear “scopes” para realizar búsquedas personalizadas. Los pasos para crear los scopes lo pondré en el siguiente post.

Como nota adicional si se utiliza Sharepoint Services pareciera que es necesario y prudente levantar los servicios de búsqueda antes de crear los Site Collections. Para levantar estos servicios es necesario utilizar el Portal Central de Administración de Sharepoint y en la sección Operaciones -> Servicios del Servidor, aquí levantar todos los servicios de búsqueda.

Gracias a Pigosky y Eugene.

16 oct 2009

Internet Explorer puede pedirle una contraseña

Recientemente mi amigo Pigosky ha tenido problemas con una aplicación web, desarrollada en .Net con la autenticación anónima des-habilitada en el IIS. El problema consistía en que el Internet Explorer solicitaba las credenciales del usuario, en lugar de inferir y enviar el token de seguridad por si mismo. Esto lo hacia de manera irregular, alguna veces si o otras veces no, un comportamiento intermitente e impredecible.

Durante la investigación encontró este artículo sobre el porque el IE podría solicitar la contraseña en este tipo de circunstancias.

Sin embargo a pesar de que el articulo se enumeran variadas posibilidades, ninguna de ellas resultó ser la causante del problema para este caso en particular. Al final el problema radicaba en una mala configuración del pool en el que se encontraba la aplicación web (los pool es una característica implementada a partir del IIS 6), la solución fue crear un nuevo pool basado en el default y colocar en este la aplicación.

5 oct 2009

Manteniendo la Posición

En .Net 1.1 existía una propiedad de las paginas .aspx que se llamaba SmatNavigation y que tenia la función de determinar si estaba o no habilitados los desplazamientos inteligentes. Entiéndase estos como que entre cada post back de la pagina se mantuviese la posición del scroll. Esto es especialmente útil en paginas con mucho contenido en el que el usuario tiene que "scrollear" y en media pagina hacer postback. No debería por que tener que volver a bajar hasta la posición en la que ya estaba.

Pues bien resulta que jubilaron prematuramente a la propiedad smartNavigation, está obsoleta desde la versión de .Net 2.0. Si queremos que nuestros usuarios no se quejen por que pierden la posición cada vez que hacemos postpack debemos usar la propiedad MaintainScrollPositionOnPostBack (así o un poco mas especifico el nombre?)

De manera que si queremos que aplique para todo un sitio, nos vamos al web.config y en la sección < Pages > añadimos el atributo:
       MaintainScrollPositionOnPostBack = "true"

Si lo necesitamos a nivel de pagina, sería:
<%@Page MaintainScrollPositionOnPostBack = "true" %>

Y finalmente si tuviéramos que definirlo a nivel de código, probablemente en el método Load de la página, utilizaríamos:
System.Web.UI.Page.MaintainScrollPositionOnPostBack = true;

A tener en cuenta entonces.

25 sept 2009

Restar una Fecha en C#

¿Cómo restamos dos fechas de manera que nos devuelva en número de días la diferencia? Yo lo hice de la siguiente manera:

    DateTime fechaMayor = DateTime.Parse("21/10/2009");
    DateTime fechaMenor = DateTime.Parse("01/10/2009");

    TimeSpan diferencia = fechaMayor.Subtract(fechaMenor);
    int diasDiferencia = diferencia.Days;

En el ejemplo debería quedar en la variable diasDiferencia el numero 20.

1 sept 2009

Dar permisos a objetos en la BD

Hay tareas que son repetitivas sobre las cuales siempre hay que pensar "Debe haber una mejor forma que hacer esto que uno por uno". Pues una de esas es asignar permisos a las tablas o vistas de una base de datos.

Para esto me encontré y modifique el siguiente script para aplicar los permisos de todas las tablas (que comiencen con cierto prefijo) y vistas de una base de datos a un rol, aplica para SQL SErver 2005:

-- Declaracion de variables para el cursor
 DECLARE @sql nvarchar(255),
 @name varchar(250),
 @type varchar (250)

/*PERMISOS PARA EL ROL QUE QUEREMOS*/
 -- Declaración del cursor
 DECLARE cPermisos CURSOR FOR

 --Para esto caso se usa el prefijo SEC para las tablas de seguridad y GEN para las generales
 SELECT table_name ,table_type FROM INFORMATION_SCHEMA.TABLES 
 WHERE table_name like 'SEC%' or table_name like 'GEN%' 

 -- Apertura del cursor
 OPEN cPermisos

 -- Lectura de la primera fila del cursor
 FETCH cPermisos INTO @name, @type

 WHILE (@@FETCH_STATUS = 0 )
 BEGIN
  if (@type = 'BASE TABLE') begin
   --Permisos a las tablas para el rol que necesitemos
   SET @sql = 'GRANT SELECT, INSERT, DELETE, UPDATE ON ' + @name + ' TO ROL_ASIGNADO'

   --Ejecuta la sentencia sql
   EXEC sp_executesql @sql 
  end
  if (@type ='VIEW')begin
   --Permisos a las vistas al rol que necesitemos
   SET @sql = 'GRANT SELECT ON ' + @name + ' TO ROL_ASIGNADO'

   --Ejecuta la sentencia sql
   EXEC sp_executesql @sql 
  end

  -- Lectura de la siguiente fila del cursor
  FETCH cPermisos INTO @name,@type
 END 

 -- Cierre del cursor
 CLOSE cPermisos

 -- Liberar los recursos
 DEALLOCATE cPermisos

En este ejemplo al Rol al que le asignamos los permisos deseados se llama ROL_ASIGNADO, obviamente lo reemplazamos para el rol que necesitamos según cada caso particular.

Para el caso de los store procedures es bastante similar
-- Declaracion de variables para el cursor
 DECLARE @sql nvarchar(255),
 @name varchar(250)

 -- Declaración del cursor para los permisos de los procedimientos almacenados que no sean de reportes
 DECLARE cPermisos CURSOR FOR

 SELECT name  FROM SAPEX.dbo.sysobjects WHERE xtype = 'P' 

 -- Apertura del cursor
 OPEN cPermisos

 -- Lectura de la primera fila del cursor
 FETCH cPermisos INTO @name 

 WHILE (@@FETCH_STATUS = 0 )
 BEGIN

  --Permisos a las tablas para el rol que se nos asigne o hayamos creado
  SET @sql = 'GRANT EXECUTE ON ' + @name + ' TO ROL_ASIGNADO'

  --Ejecuta la sentencia sql
  EXEC sp_executesql @sql 

  -- Lectura de la siguiente fila del cursor
  FETCH cPermisos INTO @name
 END 

 -- Cierre del cursor
 CLOSE cPermisos

 -- Liberar los recursos
 DEALLOCATE cPermisos

Estos dos script, modificados según nuestras necesidades, nos puede hacer la vida más sencilla cuando se trata de asignar permisos a los objetos de la base de datos.

20 ago 2009

Escribiendo un archivo de texto en Web

Por alguna razón, necesitaba escribir un archivo de texto a modo de Log con mensajes desde una aplicación web, y dejarlo dentro de la propia estructura del sitio en una carpeta especial parametrizable por web.cofig, y el nombre del archivo siempre debía ser Log.txt

Primeramente en el proyecto que usemos para almacenar las clases utilitarias creamos una nueva o reutilizamos alguna afín. Creamos un método que invocar cuando queramos usar para registrar los mensajes. En la clase que usemos debemos incluir el siguiente import
using System.IO;

y en el método lo siguiente:
String rootPath = System.Web.HttpContext.Current.Request.PhysicalApplicationPath;
string pathLog = ConfigurationSettings.AppSettings["PATHLOG"];
if (!string.IsNullOrEmpty(pathLog))
{
    pathLog = Path.Combine(rootPath, pathLog);
    pathLog = Path.Combine(pathLog, "Log.txt");

    if (!File.Exists(pathLog))
    {
          using (StreamWriter sw = File.CreateText(pathLog))
          {
                sw.WriteLine("Archivo creado " + DateTime.Now.ToString());
                sw.WriteLine();
          }
    }

    using (StreamWriter sw = File.AppendText(pathLog))
    {
          sw.WriteLine("");
          sw.WriteLine("NUEVO EVENTO______Fecha y Hora:" + DateTime.Now.ToString());
          sw.WriteLine("");
          sw.WriteLine("DESCRIPCION:");
          sw.WriteLine("");
          sw.Write(Mensaje);
          sw.WriteLine("");                 
    }
}

Con esta instruccion System.Web.HttpContext.Current.Request.PhysicalApplicationPath encontramos el path fisico donde se encuentra nuestra aplicación web y por medio de Path.Combine vamos formando el path completo donde queremos dejar nuestro archivo.

Por medio del StreamWriter creamos el archivo si no existe (CreateText) y comenzamos a escribir(AppendText) en el mismo. En este ejemplo la variable Mensaje se pasa por parámetro al método con el texto que queremos dejar en nuestro Log.

4 ago 2009

Javascript desde C#

Hace poco necesitaba abrir una ventana emergente luego de dar clic en botón en una página aspx, he inmediatamente después re-direccionar la pantalla "padre" a otra dirección. Para cumplir estos objetivos se me ocurrió registrar un script en el evento del botón (obviamente después de ejecutar/procesar lo que tenía que hacer del lado del servidor) lo cual me quedó mas o menos así:
...

    StringBuilder script = new StringBuilder();
    script.Append(" &lt; script language= ");
    script.Append(Convert.ToChar(34));
    script.Append("javascript");
    script.Append(Convert.ToChar(34));
    script.Append("&gt; ");
    script.Append("window.open(");
    script.Append(Convert.ToChar(34));
    script.Append("PaginaPopup.aspx?Parametro1=");
    script.Append(intParametro1.ToShortDateString());
    script.Append("&amp;Parametro2=");
    script.Append(intParametro2.ToString());  
    script.Append(Convert.ToChar(34));
    script.Append(",");
    script.Append(Convert.ToChar(34));
    script.Append(Convert.ToChar(34));
    script.Append(",");
    script.Append(Convert.ToChar(34));
    script.Append("Width: 400px; Height: 250px; scroll: no; status:yes;");
    script.Append(Convert.ToChar(34));
    script.Append(");");
    script.Append(" window.location=");
    script.Append(Convert.ToChar(34));
    script.Append("PaginaRedireccionar.aspx");
    script.Append(Convert.ToChar(34));
    script.Append(";");        
    script.Append(" &lt; / script &gt;");
        
    ClientScript.RegisterClientScriptBlock(this.GetType(), "popup", 
script.ToString());

...


En este ejemplo usé un StringBuilder para ir creando el script; se utiliza un window.open para abrir la página PaginaPopup.aspx con dos parámetros (Parametro1 y Parametro2) y especificamos las características de la ventana a abrir. Seguidamente usamos window.location para re-direccionar la ventana "padre" a la página PaginaRedireccionar.aspx

finalmente registramos este script por medio del metodo ClientScript.RegisterClientScriptBlock y misión cumplida!

23 jul 2009

¿Múltiples Webservices o un único con muchos Webmethods?

Mi amigo Natacuin, gran programador aunque últimamente más del lado de la administración, planteo hace poco esa pregunta: se debe utilizar un único webservices con muchos webmethods o es preferible por rendimiento partirlos en varios webservices con menos webmethods.

El mismo Natacuin buscó su respuesta planteándose un escenario y una serie de pruebas reseñables las cuales se encuentran en esta entrada de su blog para consulta y mayor detalle.

La conclusión a la que llegó es que "Las diferencias existen pero no son abismales, lo que significa que para que el rendimiento sea un factor a tomar en cuenta, los request por segundo al sistema deben ser miles ". Y que la recomendación es que es preferible usar múltiples webservices, lo cual también puede redundar en otros beneficios como en un mayor orden ya que se pueden agrupar en un webservice los webmethod afines, logrando asu vez facilidad de mantenimiento e incluso de programación por cuanto se pueden atender varios webmethods por varias personas sin pensar en la consistencia del código en el caso de que no se contase con una herramienta que lo controle por nosotros.

11 jul 2009

OLEDB para cargar archivos de excel

Recientemente debíamos cargar un archivo al servidor y cargar sus celdas en una base de datos. Ademas debíamos cumplir el requerimiento de no instalar ningún componente de MS Office en los servidores.

Como sugerencia del departamento de sistemas del cliente se nos indicó que utilizáramos Dtsx para realizar esa operación y de hecho llegamos un solución con paquetes SSIS. Sin embargo se nos complicó por una cuestión de permisos. Finalmente optamos por hacer una solución basada en OleDB más sencilla y más fácil para darle mantenimiento.

¿Y el rendimiento? pues ninguna diferencia porque resulta que los Dtsx para manejar excel utiliza OleDb. He aquí parte de la solución con .Net y OleDB

Primeramente recordar usar este using:
 using System.Data.OleDb;

Todo el secreto esta en la conexión
   OleDbConnection cnnOle = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:Pruebas\test.xls;Extended Properties=\"EXCEL 8.0;HDR=NO;IMEX=1\";");
   cnnOle.Open();  

Aqui hay varios aspectos a tomar en cuenta:
- Primeramente el Data Source puede ser tanto una carpeta local, como en este caso, o bien una carpeta compartida o DFS (p.e. \\DFS\Pruebas\test.xls)

- Lo que vamos a hacer es una consulta SQL del Excel por lo que si en Extended Properties indicamos que HDR=YES, estamos diciendo que el primer valor de las celdas es el nombre con el que se conoceran las columas. Asi si tenemos un excel en el primer valor de cada columna, o encabezado, es PRODUCTO, CANTIDAD, PRECIO entonces podriamos hacer:
SELECT PRODUCTO, CANTIDAD, PRECIO FROM [Sheet1$]
Pero si usamos HDR=NO, usamos el nombre generico (F1, F2.,..) de las columnas, incluyendo, obviamente, el primer valor de cada columna como valores que nos devolvera la consulta

-Ademas debemos tener en cuenta que el IMEX=1 en Extended Properties significa que se tomaran los valor tal y como se FORMATEAN en las celdas, esto es muy importante porque por ejemplo si el valor de la celda es un número, supongamos 1000000 y se encuentra formateado como monto (1,000,000.00) es posible que sea mejor eliminar el tag IMEX del todo para usar el valor real de la celda en lugar del ya formateado para evitar errores de conversión de tipos.

Seguidamente hacemos la consulta:
DataTable schemaTable = cnnOle.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });

OleDbCommand cmdOle = new OleDbCommand("SELECT F1,F2,F3  FROM [" + schemaTable.Rows[0]["Table_Name"].ToString() + "]", cnnOle);

OleDbDataAdapter daOle = new OleDbDataAdapter(cmdOle);
dt = new DataTable();
daOle.Fill(dt);


La primera instrucción lo que hace es extraer en un DataTable el schema del excel para poder sacar el nombre de las hojas, esto es importante porque en el si Excel esta en español las hojas se denominan Hoja1$, Hoja2$, etc.. y si esta ingles Sheet1$, Sheet2$, etc ademas puede tener un nombre personalizado y con esta instrucción podemos desentendernos de este aspecto.

Las demás instrucciones realizan la consulta usando OleDbCommand y OleDbDataAdapter para dejar la información que necesitamos en un DataTable y a partir de que lo podemos manipular como mejor nos convenga

Gracias una vez más a Pigo Sama por su inmensa sabiduría.

18 jun 2009

Ubuntu puesta al día

Por febrero del presente año, de nuevo había escuchado el llamado...Entonces comencé con el Ubuntu 8.10 Intrepid, dejándome un grato sabor de boca.
Hoy cinco meses después toca una puesta al día de mi ubuntesca incursión. Comencemos por el cambio, Sí, cambié de 8.10 intrepid a Ultimate 2.0. ¿La razón? La desconexión.

Como no pude hacer que Ubuntu reconociera mi winmodem, pues no me podía conectar a Internet, como no me podía conectar pues las actualizaciones eran un dolor de cabeza. Navegando por Internet me encontré con el Ubuntu Ultimate Gamers Edition para ser exactos. Desinstalé mi intrepid e instalé la nueva adquisición y "voila!" TODOS los codec de vídeo (lo que más necesitaba) estaban listos y armados. Nada de andar buscando paquetes adicionales ni dependencias. Perfecto!


Después me di cuenta que mi hacía falta los necesarios para ver video rmv pero era un mal menor que puede superar rápidamente. Ahorita puedo ver todos mis capítulos de anime,escuchar mis mp3, tengo mi suite de ofimática (OpenOffice), tengo máquinas virtuales de windows montadas sobre VirtualBox para mi trabajo en .Net todas comunicándose por virual networking, instalados los drivers Nvidia, juego ocasionalmente con el gridwars o Chromium y hasta ocurrió el milagro de poder instalar mi nuevo winmodem (el anterior pasó a mejor vida) este post lo hice desde Ubuntu!

Conclusión: Aunque el cambio es algo brusco y no muy fácil, voy aprendiendo, ya se me va quitando el miedo a la terminal, aunque a veces tengo que hacer una pausa para pensar ¿que estoy haciendo? ya que no se trata de copiar y pegar en la consola si no tratar de entender que estoy haciendo. Sin embargo en términos generales estoy muy satisfecho con el Ultimate, facilita bastante esa transición, aunque aún tengo Windows XP instalado para jugar el COD Modern Warfare :-)

12 jun 2009

Null y DBNull hermanos pero no gemelos

Hace poco me encontré con que este código
Total= (monto_evaluar== null) ? 0 : Convert.ToDecimal(monto_evaluar);
genera el error:

Object cannot be cast from DBNull to other types

Resulta que la variable monto_evaluar estaba declarada como object y ademas se obtenía directamente de un Dataset. Pues bien cuando se hacen cosas como estas hay que tener en mente que desde la base de datos en caso de que el dato este nulo, no se devuelve un null si no un tipo dato DBNull. Ellos son hermanos pero no gemelos, significan prácticamente lo mismo pero son tipos de dato diferentes y, adicionalmente, si un objeto es de tipo DBNull no se puede castear (convertir) en ningún otro tipo ni siquiera para comparar y éste es precisamente el error que se nos muestra.

La línea de código debe quedar como sigue:
Total=(monto_evaluar== System.DBNull.Value) ? 0 : Convert.ToDecimal(monto_evaluar);

De esta forma comparamos utilizando el tipo de dato correcto.

25 may 2009

Cómo pasar variables a un DTSX

Recientemente he tenido que "agarrarme" (como decimos en Tiquicia) con los Dtsx (la evolución de los tan útiles Dts de Sql Server 2000 ahora enmarcado en los paquetes SSIS de Sql Server 2005).

La cuestión es que ya cargado dentro de un componente .Net necesitaba pesarle el valor de unas variables que había declarado dentro del Dtsx como si de parámetros se tratasen. Cumplí este cometido de la siguiente manera:

Se requiere el siguiente using para trabajar con Dtsx:
using Microsoft.SqlServer.Dts.Runtime;

y luego el siguiente código como parte del método correspondiente:
Package myPackage;
Application integrationServices = new Application();
string packagePath= System.Configuration.ConfigurationManager.AppSettings["DTSXPATH"];
myPackage = integrationServices.LoadPackage(packagePath, null);

Variables vars = null;
myPackage.VariableDispenser.LockForWrite("variable1");
myPackage.VariableDispenser.LockForWrite("variable2");
myPackage.VariableDispenser.GetVariables(ref vars);
try
{
    foreach (Variable v in vars)
    {
        switch (v.Name)
        {
            case "variable1":
                v.Value = "lo que quiera pasar para la variable1";
                break;
            case "variable2":
                v.Value = "lo que quiera pasar para la variable2";
                break;
        }

    }
}
catch (Exception ex)
{
    throw ex;
}
finally
{
    vars.Unlock();
}
DTSExecResult local_DTSExecResult = myPackage.Execute();
if (local_DTSExecResult == Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure)
{
    foreach (DtsError local_DtsError in myPackage.Errors)
    {                
        throw new Exception(local_DtsError.Description);
    }
}

Variable1 y Variable2 son variables globales a nivel del dtsx las cuales por medio del VariableDispenser son accesibles tanto de escritura como en este caso o de lectura si se requierese. Siempre hay que hacer un Unlock de las variables antes de ejecutar el paquete .

Otra cosa interesante es la detección de errores de DTSX dentro de ambiente .Net, Éstos se almacenan en la colección Errors del paquete. En este caso se dispara una excepción cuya descripción es la del primer error en dicha colección pero se puede hacer de forma más elaborada almacenando toda la colección de errores usando la instrumentación para ello, por ejemplo.

15 may 2009

chr() en C#

En algunas ocasiones las viejas funciones las echamos de menos cuando intentamos hacer algo similar en los nuevos lenguajes. En esta ocasión particular necesito utilizar el chr() de visual basic pero en C#. El chr lo que hace es que devuelve el caracter equivalente al código ascii que le pasemos por parámetro
La función en C# equivalente vendría siendo Convert.ToChar() de manera que si que quiere concatenar un salto de línea a un texto, por ejemplo, sería así:
variable_texto + Convert.ToChar(10)
Algunos códigos ASCII útiles para tener a mano
10 = Saldo de linea
13 = Retorno de carro
32 = Espacio
34 = Comillas dobles
39 = Comilla simple
o podemos consultar http://www.ascii.cl/es/
La función inversa del chr() en VB es asc() en la cual le pasamos un caracter y nos devuelve el código ascii correspondiente. En C# podría reemplazarse por Convert.ToInt32() pasando por parámetro en carater a evaluar.
Por ejemplo:
char c = '"';
Console.Write(Convert.ToInt32(c));
Esto nos devuelve 34 que es el código ascii de la comillas dobles. Sin embargo hay caracteres cuyo código ascii no se puede determinar de esta manera y requiere una forma mas elaborada. Quizás en un próximo post deje una función que emule el asc, de momento me ha funcionado bien Convert.ToInt32().
Como nota adicional en T-SQL la función CHAR() es la equivalente a chr() y ASCII() a asc().

11 may 2009

Cerrar Ventanas

En una página se abren varias ventanas popups para recabar información variopinta, pero deseo que si me salgo de la pantalla que abre esas ventanas, éstas no queden desperdigadas, si no que también se cierren.

Hago en javascript lo siguiente:


var VentanaIndex = 0;
var VentanasHijas = Array();
function cerrarVentanas()
{
if (VentanasHijas.length > 0)
{
for (var n=0; VentanasHijas.length; n++)
{
VentanasHijas[n].close();
}
}
}

function AbrirVentana(sURL, sName, sFeatures, bReplace)
{
VentanasHijas[VentanaIndex] = window.open(sURL, sName, sFeatures, bReplace);
VentanaIndex++;
return VentanasHijas[VentanaIndex];
}


En el html, el encabezado del body colocamos lo siguiente:


body onunload="cerrarVentanas();"


Cada vez que necesitemos abrir una ventana lo hacemos por medio de la función AbrirVentana así incrementamos el arreglo de ventanas que después cerramos si aun están abiertas a la hora de cerrar la pagina padre.

No necesitamos decrementar la variable VentanaIndex cuando cerramos los popups pues javascript no da error si la ventana a cerrar ya no existe.

5 may 2009

Windows 7

Ya esta disponible la version candidata para producto final (Release Candidate, RC) del nuevo Windows 7. Esta version es una mejora mucho más estable y depurada que la anteriormente liberada versión beta.

Al respecto de Windows 7, una de las cosas que más me llama la atención es la virtualización "completa" de XP (o en teoría de cualquier sistema operativo). Según el artculo de Coding Horror Has The Virtualization Future Arrived? las aplicaciones nativas de XP se ejecutaría en una especie de maquina virtual pero siempre dentro del contexto de Windows 7 osea sin necesidad explícitamente de levantar,por ejemplo, virtual PC. Para el usuario seria transparente, como ejecutar cualquier otra aplicación.

Esto no es nuevo, ni mucho menos. Hace tiempo que ya lo viene haciendo Apple en sus OS X, pareciera que una vez más Microsoft busca en sus competidores tecnologías con las cuales "sorprender" a sus usuarios. Esperemos, si embargo, que esta vez la implementación sea la correcta, ya que desde ahora muchos se preguntan por cuestiones como el desempeño, o la implementación de drivers de video adecuados dentro de la virtualización, para el uso en los videojuegos por ejemplo.

En todo caso aqui tiene el link para comenzar a descargar el Windows 7 RC y veremos si con este nuevo wndows ya pasan la página del tristemente célebre Vista cuyo destino parece ser pasar con mucha más pena que gloria por la páginas (y las pc´s) de la historia de la informática.

23 abr 2009

De Arreglo de bytes a Archivo

Hace un par de días subí como convertir una archivo a un arreglo de bytes. Ahora toca la operación inversa. Es realmente muy sencillo, lo único que debemos tener en cuenta son las cuestiones de permisos a la hora de guardar el archivo en nuestra estructura de directorios, pero para esto ya podemos impersonar temporalmente.
protected void guardaArchivo(byte[] archivo, string nombreArchivo)
{
    string excel_path = ConfigurationManager.AppSettings.Get("EXCELPATH");      
    string excel_file = nombreArchivo;

    try
    {
      excel_path = excel_path + excel_file;

      FileStream archivo_fisico = new FileStream(excel_path, FileMode.Create, FileAccess.Write);
      foreach (byte b in archivo)
      {
          archivo_fisico.WriteByte(b);
      }
      archivo_fisico.Close();
    }
    catch (Exception ex)
    {
      throw ex;
    }     
}
Ya con esto cerramos el circulo de pasar un archivo por diversas capas físicas si fuera necesario.


EDITO:
Felipo me indico que otra forma de hacerlo puede ser
byte[] buffer; //parameter

using (FileStream newFile = new FileStream(newFilePath, FileMode.Create, FileAccess.Write))
{
    newFile.Write(buffer, 0, buffer.Length);
    newFile.Flush();
    newFile.Close();
}

Gracias Felipo.

21 abr 2009

De Archivo a arreglo de bytes

La cuestión era que necesitaba tomar un archivo de un control FileUpload de ASP.Net y transformarlo a un arreglo de bytes y pasárselo a un web service que hiciera el proceso inverso (pasar del arreglo de bytes al archivo) . Así fue como resolví la primera parte:

Primero un import de System.IO seguidamente algo similar a esto en el método en que se requería:
Stream sr = fupArchivo.PostedFile.InputStream;
int Length = 256;
byte[] buffer = new byte[Length];
ArrayList list = new ArrayList();
int bytesRead = sr.Read(buffer, 0, Length);
while (bytesRead > 0)
{
     foreach (byte b in buffer)
     {
          list.Add(b);
     }
     bytesRead = sr.Read(buffer, 0, Length);
}
byte[] Archivo = (byte[])list.ToArray(typeof (byte));
webservice.cargaArchivo(Archivo);

En un próximo post el proceso inverso.

1 abr 2009

Cómo crear una red virtual con Virtual PC

Supongamos que tenemos una máquina virtual con nuestro ambiente de desarrollo y otro máquina virtual con una base de datos. Queremos que ambas máquinas funcionen como si estuvieran en red junto con el host. Sin embargo nuestro host no tiene tarjeta de red o la tiene pero no tiene acceso a ninguna red en este momento.

A continuación una guía paso a paso para windows XP como host y virtual PC 2007:

Primero necesitamos instalar en el equipo host el Adaptador de Bucle Invertido de micrsoft (Micrsoft Loopback Adapter):
  • Vamos a Panel de control -> agregar hardware.
  • En el wizard que nos sale indicamos que ya hemos conectado el hardware, en la siguiente pantalla elegimos la ultima opción "Agregar un nuevo dispositivo de hardware", en la siguiente "Instalar el hardware seleccionándolo manualmente de una lista(avanzado)". Seguidamente seleccionamos adaptadores de red. Finalmente elegimos el fabricante Microsoft y Adaptador de bucle invertido de Microsoft y completamos la instalación.

Con lo anterior instalamos una especie de tarjeta de red virtual en nuestro equipo, por lo que si inmediatamente le damos botón derecho sobre Mis Sitios de Red -> Propiedades, Nos debe aparecer una nueva conexión de red que utiliza el adaptador que acabamos de instalar. Esta conexión la configuramos de manera que no choque con alguna otra conexión (si tenemos otra activa) Por ejemplo como sigue:


Una vez finalizada esta conexión iniciamos virtual PC 2007 y vamos a la configuración de red de las máquinas virtuales y elegimos que utilicen el adaptador de bucle invertido:


Seguidamente iniciamos las máquinas virtuales y vamos a las conexiones de red y configuramos el TCP/IP para que pueda ver a las otras maquinas. Por ejemplo:


Debe asegurarnos que todas las máquinas pertenezcan al mismo dominio o grupo de trabajo, tanto el host como las máquinas virtuales para que puedan "verse" entre si. Esto debería bastar para montar una red virtual con Virtual PC 2007.

23 mar 2009

Uno de esos...

Recientemente fui víctima de uno de esos errores. Uno de esos que es esquivo, muy extraño y casi podríamos decir malicioso. Estaba haciendo una aplicación winform, una utilidad de encripción de cadenas de texto y todo funcionando perfectamente, en el ambiente de desarrollo, pero cuando me llevo el .exe para otra pc se "esponjó". Conforme intentaba ejecutar se caía.

¿Cómo una aplicación con un solo form y unas cuantas lineas de código se puede caer tan estrepitosamente? En el event log se vei lo siguiente:

Un maravilloso System.NullReferenceException

Un poco de buscar en internet y nada, pero en alguna de esas referencias estaba la pista. Yo en mi program.cs que levantaba la form tenia algo como:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());

Pues bien Resulta que comentado la linea Application.SetCompatibleTextRenderingDefault(false); ya funcionó.

Si VS 2005 esta instalado en la máquina destino no van a tener ningun problema, pero si no, es mejor comenzar a comentar esa linea.

17 mar 2009

Fecha con hora cero

Alguna vez me pasó que un procedimiento almacenado en SQLServer 2005 recibe un parámetro tipo datetime. A lo hora de recibirlo vienen fecha y hora, pero yo necesitaba unicamente la fecha sin la hora, o sea guardar, la fecha pero con hora cero. Para esto recientemente me topé con la siguiente instrucción :

(dateadd(dd,0, datediff(dd,0,@FechaYHora))

Esto me devuelve la fecha con hora 00:00:00 de la variable @FechaYHora. Tambien es muy util si se quiere comparar estos tipos de datos en una clausula where por ejemplo.

13 mar 2009

10-4

Para los que no sabían, nadie les ha contado o no les avisaron, ya vienen juntos y revueltos el Visual Studio 2010 y el .Net Framework 4.0 ¡Wow!

Ahora bien, no se si soy parte de una minoría, aparentemente es así, pero en el trabajo yo continuo desarrollando con el VS2005 y el Framework 2.0, largos proyectos, contratos, clientes, etc... así lo exigen. No tengo mucho tiempo para dedicarme a investigar o estar al día con las versiones recientes. Si bien es cierto que tengo instalado el VS2008 en mi casa, no le sacado mucho provecho que digamos.

Pero bueno, conmigo o sin mi, Microsoft ya pasó por los frameworks 1.0 y 1.1, primeros intentos de introducir y estabilizar la plataforma; 2.0, mejora estabilidad del IDE y mejoras sensibles y sobre todo visibles en el ADO.Net; 3.0, fundations (WCF, WF, WPF); 3.5 revision del 3.0 con Linq, algunas mejoras en asp.net y creo que un framework de entidades; y ahora el 4.0 (?).

En todo caso les dejo algunos links de interés:
Veremos que nos depara el futuro, 10-4, cambio y fuera...

4 mar 2009

Impersonar Temporalmente

¿Alguna vez les ha pasado que están desarrollando un web service y requieren acceder a una carpeta compartida? ¿No? pues a mi me pasó. Necesitaba dejar un archivo en una carpeta compartida que de paso ya tenia un usuario y una contraseña particular para este tipo de tareas, por lo que no queda mas remedio de impersonar temporalmente.

¿Como? haciendo una clase sobrepuesta a las dlls de Windows que se requieren para tal propósito. Siguiendo este articulo se puede crear una clase similar a la siguiente que sirva a nuestros intereses:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;


namespace Foyland.Comun
{
//Esta clase impersonaliza un usuario temporalmente
public class Impersonalizacion
{
enum LogonSessionType : uint
{
Interactive = 2, //Esta tiene permisos sobre los recursos de red
Network, //Esta No tiene permisos sobre los recursos de red (curioso no?)
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}

enum LogonProvider : uint
{
Default = 0, // default (usar esta)
WinNT35, // usa una señales dummy para autenticar (sends smoke signals to authority)
WinNT40, // usa NTLM
WinNT50 // usa Kerberos o NTLM
}

[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonSessionType logonType,
LogonProvider logonProvider,
out IntPtr token);

[DllImport("advapi32.dll", EntryPoint = "DuplicateToken", ExactSpelling = false,
CharSet = CharSet.Auto, SetLastError = true)]

public static extern int DuplicateToken(
IntPtr ExistingTokenHandle,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle);

public static WindowsImpersonationContext WinLogOn(
string strUsuario,
string strClave,
string strDominio)
{
IntPtr tokenDuplicate = new IntPtr(0);
IntPtr tokenHandle = new IntPtr(0);
if (LogonUser(strUsuario, strDominio, strClave, LogonSessionType.Interactive,
LogonProvider.Default, out tokenHandle))

if (DuplicateToken(tokenHandle, 2, ref tokenDuplicate) != 0)
return (new WindowsIdentity(tokenDuplicate)).Impersonate();
return null;

}
}
}

Aqui termina la case que necesitamos para impersonar. Ahora necesitamos invocarla desde el webservice. Porción de codigo de ejemplo:

using System.Configuration;
using System.IO;
using System.Security.Principal;
.
.
.
//ya dentro del metodo apropiado

//Carga las variables que estan en el web.config
string usuario = ConfigurationManager.AppSettings.Get("USUARIO_DFS");
string password = ConfigurationManager.AppSettings.Get("PASSWORD_DFS");
string dominio = ConfigurationManager.AppSettings.Get("DOMINIO_DFS");

//crea el objeto necesario para impersonalizar
WindowsImpersonationContext _objContext = null;

//Despues de esta sentencia ya estamos impersonalizando con el usuario deseado
_objContext = Impersonalizacion.WinLogOn(usuario, password, dominio);
try
{
//Codigo para crear y guardar el archivo en la carperta compartida;
}
catch (Exception ex)
{
throw ex;
}
finally
{
//SIEMPRE debemos ejecutar el metodo Undo para regresar a las credenciales anteriores
_objContext.Undo();
}

Para lo que necesitaba funciona perfectamente.

19 feb 2009

showModalDialog y postbacks

Estábamos utilizando window.showModalDialog para abrir una popUp modal en una aplicación Asp.net, pero nos encontramos con el problema que necesitábamos manejar unos eventos postback dado la carga de algunos controles. Pues acontece que cada postback abre una nueva ventana y que esta es el comportamiento natural de una ventana abierta por medio de window.showModalDialog. La solución encontrada fue añadir la siguiente linea inmediatamente después del tag de <> en el html del popUp modal

< target="_self">

Esta solucion probamos que funciona con IE6, IE7 y Firefox 3.0

Gracias a Roger dono que estuvo lidiando con el problema.

12 feb 2009

Foy y Ubuntu

Haciendo un poco de historia. Allá por el 2005 o 2006 (no recuerdo bien) se me ocurrió la brillante idea de probar alguna de las distros de Linux, debió ser algún llamado del "lado oscuro" (¿o será al revés?). Por aquel entonces opté por SUSE. Resulta que mi pingüinesca aventura fue bastante fugaz y de sabor agridulce.

Recuerdo que lo que más molestó fue que nunca logré que reconociera el sonido (HDAudio). Debido a mi total ignorancia de Linux y a mi total dependencia de "the Windows way" pues no es muy sorprendente que me frustrará rápidamente y volviera a mi conocido entorno de Microsoft.


Pues bien resulta que últimamente he vuelto a escuchar "el llamado" y he vuelto a ceder. Ahora estoy probando Ubuntu 8.10 Intrepid basado en Debian. Aclaro que mi conocimiento de Linux sigue igual o incluso puede que haya empeorado desde el último intento pero esta vez me ha gustado más este sistema operativo.

Todo lo ha reconocido a la primera (excepto el módem, si dial up, algunos seguimos sin tener Internet de banda ancha en nuestro hogar y aun utilizamos los viejos y ruidosos módem para conectarnos) vídeo, audio, discos SATA, dvd, USB, tarjeta de red, etc.. Una maravilla para el usuario común y corriente. Todo con una instalación, la verdad se ha dicha, mucho más sencilla y más rápida que XP, ademas de la opción de ejecutarlo en vivo o sea directamente desde el cd sin instalar nada.


El sistema es rápido y se mueve con suavidad, ligero según mi percepción, con tiempos de inicio y apagado excelentes. Firefox para navegar por la red, un cliente de correo, y uno que otro utilitario interesante. Para los usuarios de procesadores de texto y hojas de cálculo viene con OpenOffice 2.4 (compatible con archivos .doc, docx, xls, xlsx, etc..), nada que enviar de la suite de Microsoft.

La instalación de programas adicionales, si la máquina esta conectada a Internet, parece sencilla (No lo he podido probar), pero, si es como en mi caso donde todo debe ser offline, no lo es, de hecho es un dolor de cabeza, te haces un nudo entre los paquetes .DEB y las dependencias entre librerías. Sin embargo hay mucha información en la red y la pagina Ubuntu Packages realmente ayuda.

Desde mi punto de vista es ideal para el usuario promedio, el usuario sencillo, esto con lo poco que llevo de usarlo. Me ha dejado una muy grata impresión y pareciera una alternaiva real al prácticamente omnipresente Windows.

10 feb 2009

Could not load file or assembly Microsoft.ReportViewer.WebForms

El error:

Could not load file or assembly 'Microsoft.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.

Se produce, entre otros motivos, cuando hacemos Copy Deployment de alguna aplicación web con reportes desplegables en el ReportViewer de Reportig Services y se debe a la ausencia de las dlls:

Microsoft.ReportViewer.WebForms.dll
Microsoft.ReportViewer.Common.dll
Microsoft.ReportViewer.ProcessingObjectModel.dll

Lo más sencillo es ubicar la aplicación ReportViewer, normalmente esta en:

C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages\ReportViewer\ReportViewer.exe

y ejecutarla en la maquina que tiene el error, con eso debería bastar.