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.