Incrustar archivos en un ejecutable

0
Filed under .NET

La idea de este artículo es mostrar cómo incrustar archivos en un ejecutable y para esto haremos de cuenta que queremos incrustar imágenes.

Generalmente tenemos que mostrar imágenes en nuestra aplicación. Imágenes que no cambiarán en tiempo de ejecución pero que sí podrán cambiar en versiones futuras de la aplicación.
Muchos de ustedes estarán imaginando posibles soluciones a este problema. Por ejemplo:

- Poner las imágenes en una carpeta dentro de la carpeta de la aplicación.
- Poner las imágenes en una tabla en una base de datos.
- Incrustar las imágenes en el ejecutable.

Como siempre en esto del desarrollo de software, no hay una solución que sea la mejor y la única para todas las situaciones posibles de negocio. Veámos algunas de las ventajas y desventajas de las soluciones propuestas anteriormente:

- Poner las imágenes en una carpeta dentro de la carpeta de la aplicación:
- Ventaja: si queremos cambiar las imágenes, digamos que para dar una nueva apariencia a la aplicación sin tener que recompilarla, simplemente vamos a la carpeta en donde están las imágenes y las remplazamos.
- Ventaja: ya que las imágenes no estarán incrustadas en el ejecutable, el tamaño del ejecutable será menor.
- Desventaja: cualquier usuario puede borrar las imágenes de la carpeta. Para hacer la aplicación más robusta, habría que validar si las imágenes no fueron borradas.
- Desventaja: cualquier usuario podría ir a la carpeta en donde tenemos las imágenes almacenadas y podría remplazarlas por otras, dejando nuestra aplicación con una apariencia inconsistente.

- Poner las imágenes en una tabla en una base de datos:
- Ventaja: las imágenes quedarían almacenadas en un solo lugar.
- Desventaja: habría que tener una base de datos con una tabla que sirva exclusivamente para almacenar las imágenes. Esto puede ser bastante molesto y hasta podría llegar a enlentecer el proceso de desarrollo de la aplicación, así como también podría agregar complejidad, etc.

- Incrustar las imágenes en el ejecutable:
- Ventaja: las imágenes quedarían almacenadas en un solo lugar.
- Ventaja: un "usuario común" no podrá cambiar fácilmente las imágenes.
- Desventaja: el tamaño del ejecutable será mayor dependiendo de la cantidad de imágenes que tengamos.
- Desventaja: si queremos cambiar las imágenes tendríamos que recompilar el ejecutable.

En el caso de querer incrustar imágenes, podemos utilizar el control "ImageList".

1 – En el explorador de solución buscamos nuestro proyecto. Para agregar un archivo de recursos, hacemos clic derecho sobre él y elegimos la opción "Add" -> "New Item…". Cuando aparezca el cuadro "Add New Item", buscamos la plantilla "Resources File" y la seleccionamos. De nombre le ponemos "Imagenes.resx" y hacemos clic en "Add". Se abrirá "Imagenes.resx".

[Haga clic en la imagen para ampliar]


2 – En "Imagenes.resx", elegimos la opción "Images". Por último, desplegamos "Add Resource" y elegimos "Add Existing File..". Buscamos una imagen y la abrimos.

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


3 – Guardamos los cambios.

Para mostrar la imagen en un botón por ejemplo, podemos utilizar el siguiente código:

    using System.Drawing;
    using System.Windows.Forms;

    public partial class Form1 : Form
    {
        public Form1()
        {
            this.InitializeComponent();
        }

        private void Form1_Load(object sender, System.EventArgs e)
        {
            Button boton = new Button();
            boton.AutoSize = true;
            boton.Image = Imagenes.button_ok;
            boton.ImageAlign = ContentAlignment.MiddleLeft;
            boton.Text = "¡Hola mundo!";
            boton.TextImageRelation = TextImageRelation.ImageBeforeText;
            this.Controls.Add(boton);
        }
    }

[Haga clic en la imagen para ampliar]


Para reproducir un sonido cuando el usuario haga clic en el botón, podemos agregar un nuevo archivo de recursos llamado "Sonidos.resx" y en lugar de elegir la categoría "Images" elegimos "Audio".

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]

    using System.Drawing;
    using System.Media;
    using System.Windows.Forms;

    public partial class Form1 : Form
    {
        public Form1()
        {
            this.InitializeComponent();
        }

        private void Form1_Load(object sender, System.EventArgs e)
        {
            Button boton = new Button();
            boton.AutoSize = true;
            boton.Image = Imagenes.button_ok;
            boton.ImageAlign = ContentAlignment.MiddleLeft;
            boton.Text = "¡Hola mundo!";
            boton.TextImageRelation = TextImageRelation.ImageBeforeText;
            boton.Click += new System.EventHandler(this.Boton_Click);
            this.Controls.Add(boton);
        }

        private void Boton_Click(object sender, System.EventArgs e)
        {
            using (SoundPlayer windowsShutdown = new SoundPlayer(Sonidos.Windows_Shutdown))
            {
                windowsShutdown.Play();
            }
        }
    }

¿Cómo?: dibujar texto sobre una imagen (GDI+)

0
Filed under .NET

Requisitos:
- .NET Framework 2.0
- Tener una referencia en nuestro proyecto al ensamblado "System.Drawing".

Código:

using System.Drawing;

//Creamos un objeto Image especificando la imagen que queremos modificar.
Image imagen = Image.FromFile(@"C:\Imagen.png");
//Creamos un objeto Graphics (superficie de dibujo) a partir de la imagen cargada anteriormente.
Graphics superficieDibujo = Graphics.FromImage(imagen);
//Creamos un objeto Font para la fuente Verdana con un tamaño de 24.
Font fuente = new Font("Verdana", 24F);
//Dibujamos la cadena de texto "¡Hola mundo!", de color verde, en la ubicación 0,0.
superficieDibujo.DrawString("¡Hola mundo!", fuente, Brushes.Green, PointF.Empty);
//Guardamos la imagen.
imagen.Save(@"C:\Imagen_modificada.png");
//Liberamos recursos.
fuente.Dispose();
superficieDibujo.Dispose();
imagen.Dispose();

¿Cómo?: validar las credenciales (nombre de usuario y contraseña) del usuario contra Windows y/o Active Directory Domain Services

0
Filed under .NET

Requisitos:
- .NET Framework 3.5
- Tener una referencia en nuestro proyecto al ensamblado "System.DirectoryServices.AccountManagement".

Código:

using System.DirectoryServices.AccountManagement;

Para validar las credenciales del usuario contra Windows (MSAM):

using (PrincipalContext samContext = new PrincipalContext(ContextType.Machine))
{
    if (samContext.ValidateCredentials("Nombre de usuario", "Contraseña"))
    {
        //Las credenciales sí son válidas.
    }
    else
    {
        //Las credenciales no son válidas.
    }
}

Para validar las credenciales del usuario contra Active Directory Domain Services (AD DS):

using (PrincipalContext domainContext = new PrincipalContext(ContextType.Domain))
{
    if (domainContext.ValidateCredentials("Nombre de usuario", "Contraseña"))
    {
        //Las credenciales sí son válidas.
    }
    else
    {
        //Las credenciales no son válidas.
    }
}

Permitir sólo una instancia en ejecución de una aplicación (How to run a single instance of an application)

1
Filed under .NET

Para esto, pueden utilizar la siguiente clase:

using System;
using System.Threading;

namespace NicolasFerreira.Utilities
{
    //* **************************************************
    //* Author: Nicolás Ferreira (http://nicolasferreira.com/)
    //* **************************************************
    /// <summary>
    /// This class uses EventWaitHandle. It lets you know if another instance of your application is already running. Also, if there is already a running instance of your application and someone try to start another, the main one will be notified.
    /// </summary>
    public static class SingleInstanceApplication
    {
        private static EventWaitHandle _EventWaitHandle;
        private static object _lock = new object();
        private static bool _Opened;
        private static Thread _WaitOneHandlerThread;
        private static bool _RaiseAnotherTriedToRunEvent;
        public delegate void AnotherTriedToRunEventHandler();
        public static event AnotherTriedToRunEventHandler AnotherTriedToRun;
        /// <summary>
        /// Gets a value that indicates whether single instance application monitor is open.
        /// </summary>
        public static bool Opened
        {
            get
            {
                return _Opened;
            }
        }
        /// <summary>
        /// Opens the single instance application monitor.
        /// </summary>
        /// <param name="uniqueInstanceName">A name that identifies your application as unique.</param>
        /// <returns>true if the system-wide EventWaitHandle could be created (the calling instance is single). false if the calling instance should terminate.</returns>
        public static bool Open(string uniqueInstanceName)
        {
            return Open(uniqueInstanceName, false);
        }
        /// <param name="local">true if each terminal server session can run an instance of your application. Otherwise, false.</param>
        public static bool Open(string uniqueInstanceName, bool local)
        {
            if (string.IsNullOrEmpty(uniqueInstanceName))
                throw new ArgumentException("uniqueInstanceName cannot be blank.");
            else if (_Opened)
                throw new InvalidOperationException("Single instance application monitor is open.");
            lock (_lock)
            {
                bool createdNew;
                _EventWaitHandle = new EventWaitHandle(
                    false,
                    EventResetMode.AutoReset,
                    string.Concat(local ? "Local\\" : "Global\\", uniqueInstanceName),
                    out createdNew);
                if (createdNew)
                {
                    _Opened = true;
                    _RaiseAnotherTriedToRunEvent = true;
                    _WaitOneHandlerThread = new Thread(new ThreadStart(EventWaitHandleHandler));
                    _WaitOneHandlerThread.Start();
                }
                else
                {
                    _Opened = false;
                    _RaiseAnotherTriedToRunEvent = false;
                    _EventWaitHandle.Set();
                    _EventWaitHandle.Close();
                    _EventWaitHandle = null;
                }
            }
            return _Opened;
        }
        private static void EventWaitHandleHandler()
        {
            if (_EventWaitHandle != null)
            {
                while (_Opened)
                {
                    _EventWaitHandle.WaitOne();
                    if ((_RaiseAnotherTriedToRunEvent) && (AnotherTriedToRun != null))
                        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state)
                        {
                            AnotherTriedToRun();
                        }));
                }
            }
        }
        /// <summary>
        /// Closes the single instance application monitor and frees resources.
        /// </summary>
        public static void Close()
        {
            lock (_lock)
            {
                if (!_Opened)
                    throw new InvalidOperationException("Single instance application monitor is not open.");
                _Opened = false;
                _RaiseAnotherTriedToRunEvent = false;
                _EventWaitHandle.Set();
                _EventWaitHandle.Close();
                _EventWaitHandle = null;
            }
        }
    }
}

Ejemplo en una aplicación consola:

        static void Main(string[] args)
        {
            SingleInstanceApplication.AnotherTriedToRun += new SingleInstanceApplication.AnotherTriedToRunEventHandler(SingleInstanceApplication_AnotherTriedToRun);
            if (!SingleInstanceApplication.Open("{63BAA9C4-849C-4b4a-8B9C-FC1D640F09E2}"))
            {
                Console.WriteLine("Another instance of this application is currently running.");
            }
            else
            {
                Console.WriteLine("This is the main instance of your application.");
                Console.ReadLine();
                if (SingleInstanceApplication.Opened)
                {
                    SingleInstanceApplication.Close();
                }
            }
        }
        static void SingleInstanceApplication_AnotherTriedToRun()
        {
            Console.WriteLine(string.Format("{0}: Another instance of this application tried to run.",
                DateTime.Now.ToString()));
        }

Ejemplo en una aplicación Windows Forms:

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            if (SingleInstanceApplication.Open("{4C8C7928-55FD-45e8-A13E-895C5EE4D0C3}"))
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
                if (SingleInstanceApplication.Opened)
                {
                    SingleInstanceApplication.Close();
                }
            }
            else
            {
                MessageBox.Show("Another instance of this application is currently running.");
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            SingleInstanceApplication.AnotherTriedToRun += new SingleInstanceApplication.AnotherTriedToRunEventHandler(SingleInstanceApplication_AnotherTriedToRun);
        }

        void SingleInstanceApplication_AnotherTriedToRun()
        {
            MessageBox.Show(string.Format("{0}: Another instance of this application tried to run.",
                DateTime.Now.ToString()));
        }

Descargar el código.

Alojar un servicio Windows Communication Foundation en Windows 7 utilizando Windows Process Activation Services (WAS)

0
Filed under .NET, Windows Communication Foundation (WCF)

Existe un error (o al menos existió al momento de escribir este artículo). Si alojamos el servicio en "Windows Process Activation Services" utilizando el protocolo "net.tcp" y el nombre del equipo contiene caracteres no-ASCII, no seremos capaces de establecer la conexión con el servicio.

Para poner en funcionamiento un servicio “Windows Communication Foundation (WCF)” hay que alojarlo. Podemos alojar los servicios en:
- Aplicaciones Windows Forms/WPF
- Aplicaciones consola
- Servicios de Windows
- Internet Information Services
- Windows Process Activation Services (WAS)

“Windows Process Activation Services” no es nuevo. De hecho fue introducido por primera vez en “Windows Vista”, está presente en “Windows Server 2008″ y continúa en “Windows 7″.
“Windows Process Activation Services” puede ser una buena opción si pensamos en correr servicios “Windows Communication Foundation” en dichos sistemas operativos.

Crear el servicio “Windows Communication Foundation”
1 – Abrimos “Visual Studio” y elegimos crear un proyecto de tipo “WCF Service Library”. De nombre le ponemos “ServicioHolaMundo”. Una vez creado el proyecto, borramos los archivos “IService1.cs” y “Service1.cs” que “Visual Studio” crea por defecto.

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


2 – Tenemos que crear un contrato (interfaz). Para esto, en el explorador de solución hacemos clic derecho sobre “ServicioHolaMundo” y elegimos “Add” -> “New Item…”. Una vez abierto “Add New Item” elegimos “Interface” y de nombre le ponemos “IServicioHolaMundo.cs”. Hacemos clic en “Add”.

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


3 – Dejamos la interfaz así:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace NicolasFerreira.Servicios
{
    [ServiceContract(Name = "ServicioHolaMundo",
    Namespace = "http://nicolasferreira.com/2009/01/09")]
    public interface IServicioHolaMundo
    {
        [OperationContract()]
        string HolaMundo();
    }
}

4 – Tenemos que crear una clase (la implementación del contrato) que implemente la interfaz “IServicioHolaMundo”. Para esto, en el explorador de solución hacemos clic derecho sobre “ServicioHolaMundo” y elegimos “Add” -> “New Item…”. Una vez abierto “Add New Item” elegimos “Class” y de nombre le ponemos “ServicioHolaMundo.cs”. Hacemos clic en “Add”.

[Haga clic en la imagen para ampliar]


5 – Dejamos la clase así:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NicolasFerreira.Servicios
{
    public class ServicioHolaMundo : IServicioHolaMundo
    {
        #region IServicioHolaMundo Members

        public string HolaMundo()
        {
            return "¡Hola mundo!";
        }

        #endregion
    }
}

6 – En el explorador de solución y bajo el proyecto “ServicioHolaMundo” buscamos el archivo “App.config”. Hacemos clic derecho sobre él y elegimos la opción “Edit WCF configuration”. Aparecerá la aplicación “Microsoft Service Configuration Editor”. En el panel “Configuration” bajo la carpeta “Services” seleccionamos “ServicioHolaMundo.Service1″. Cambiamos el valor de la propiedad “Name” por “NicolasFerreira.Servicios.ServicioHolaMundo”.

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


7 – Expandir las siguientes carpetas: “NicolasFerreira.Servicios.ServicioHolaMundo” -> “Endpoints” hasta ver dos “Endpoints” que no tienen nombre: “(Empty Name)”. Hacemos clic sobre el primero. Podemos ver que el valor de la propiedad “Binding” es “wsHttpBinding”. Lo cambiamos a “netTcpBinding”. El valor de la propiedad “Contract” es “ServicioHolaMundo.IService1″. Lo cambiamos a “NicolasFerreira.Servicios.IServicioHolaMundo”. Ahora hacemos clic sobre el segundo “Endpoint”. Podemos ver que el valor de la propiedad “Binding” es “mexHttpBinding”. Lo cambiamos a “mexTcpBinding”.

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


8 – Expandir las siguientes carpetas: “Advanced” -> “Service Behaviors” -> “ServicioHolaMundo.Service1Behavior” hasta ver “serviceMetadata”. Seleccionamos “serviceMetadata” y cambiamos el valor de la propiedad “HttpGetEnabled” a “False”.

[Haga clic en la imagen para ampliar]


9 – Cerramos la aplicación y cuando nos pregunte si queremos guardar los cambios le decimos que sí.
10 – Por último agregamos un archivo llamado “ServicioHolaMundo.svc” al proyecto.

[Haga clic en la imagen para ampliar]


11 – Lo dejamos así:

<%@ ServiceHost Language="C#" Service="NicolasFerreira.Servicios.ServicioHolaMundo" %>

12 – Compilamos la solución y cerramos “Visual Studio”.

[Haga clic en la imagen para ampliar]


Instalar y configurar “Windows Process Activation Services” para alojar el servicio
1 – En "Windows 7" vamos a "Control Panel". Una vez que estemos ahí, hacemos clic sobre el vínculo "Programs" y después bajo "Programs and Features" hacemos clic en el vínculo "Turn Windows features on or off". Cuando aparezca el cuadro "Windows Features" marcamos las siguientes opciones:
- "IIS Management Console" que se encuentra en: "Internet Information Services" -> "Web Management Tools".
- ".NET Extensibility" que se encuentra en: "Internet Information Services" -> "World Wide Web Services" -> "Application Development Features".
- "Windows Communication Foundation Non-HTTP Activation" que se encuentra en: "Microsoft .NET Framework 3.5.1"
Finalmente hacemos clic en "OK".

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


2 – Volvemos a “Control Panel” pero esta vez hacemos clic sobre el vínculo “All Control Panel Items” para así poder ir a “Administrative Tools” y elegir “Internet Information Services (IIS) Manager”.

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


3 – Por defecto en “Windows 7″ ya vienen configurados algunos “Bindings” para el sitio “Default Web Site”. Esto lo comprobamos expandiendo el nombre del equipo, luego “Sites” y luego eligiendo “Default Web Site”. Si hacemos clic en “Bindings…” veremos lo siguiente:

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


4 – Para este ejemplo utilizaremos la configuración por defecto (el protocolo “net.tcp” y el puerto “808″).
5 – Hacemos clic derecho sobre "Default Web Site" y seleccionamos la opción "Add Application…". En "Alias" escribimos "ServicioHolaMundo" y en "Physical path" escribimos "C:\inetpub\wwwroot\ServicioHolaMundo" (previamente hay que crear la carpeta "ServicioHolaMundo" en "C:\inetpub\wwwroot"). Hacemos clic en "OK".

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


6 – Una vez que hicimos clic en "OK", quedará seleccionada la aplicacion que acabamos de crear. En el panel "Actions" hacemos clic en la opción "Advanced Settings…". Aparecerá el cuadro "Advanced Settings". El valor de la propiedad "Enabled Protocols" es "http". Lo cambiamos por "net.tcp", hacemos clic en "OK" y cerramos "Internet Information Services (IIS) Manager".

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


7 – Vamos a la carpeta "C:\inetpub\wwwroot\ServicioHolaMundo" y pegamos los archivos "ServicioHolaMundo.svc" y "app.config". Renombramos el archivo "app.config" por "Web.config". Creamos una carpeta con el nombre de "Bin" y le pegamos el archivo "ServicioHolaMundo.dll".

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]


Probar el servicio
1 – Podemos probar lo que hemos hecho yendo a Visual Studio, creando un proyecto de tipo aplicación consola y agregando una referencia al servicio.

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]

[Haga clic en la imagen para ampliar]

Won’t you pour me a Cuban breeze Gretchen