Archivo de la etiqueta: c-sharp

Saber las unidades externas (USB pendrive fijo o extraíble)

Pues eso… yo pensaba que todas las unidades (de tipo pendrive) USB devolverían un valor del tipo DriveType.Removable, pero resulta que no. Al igual que ocurre con los discos duros conectados por USB, los pendrives también pueden ser de tipo fijo (DriveType.Fixed).

Y lo que te voy a mostrar es el código para C# y Visual Basic (usando .NET 5.0 con Visual Studio 2019) para saber todas las unidades conectadas por USB (sean fijas o extraíbles), además de una función para saber si el path indicado está o no en una unidad extraíble.

También incluyo un ejemplo de cómo saber las unidades (y el tipo de unidad que es) usando el «clásico» método para saber las unidades instaladas con DriveInfo.GetDrives().

En la siguiente captura puedes ver el programa funcionando en mi equipo.

Imagen 1. El programa funcionando.

En mi equipo tengo conectado 2 discos duros por usb (letras S y T), un pendrive de tipo fijo (letra F) y uno de tipo extraíble (letra G).

En la primera lista (usando el método GetDrives de la clase DriveInfo) el único que se muestra como «extraíble» es el disco G, el resto se muestran como fijos (fixed).
En la segunda lista se muestran las unidades extraíbles, sean o no fijas.

Ya sin más rollos te muestro el código tanto para C# como para Visual Basic.

El código lo puedes descargar de GitHub (UnidadesExternas), pero si quieres crearlo por tu cuenta, decirte que he tenido que añadir una referencia al paquete de Nuget System.Management versión 5.0 ya que estos proyectos los he creado con Visual Studio 2019 y esa versión de Visual Studio no utiliza versiones posteriores a .NET 5.0.

El tipo de proyecto es Consola de Windows, aunque he tenido que cambiar el tipo del proyecto, ya que al elegir que sea para .NET 5.0 ha intentado usarlo para cualquier plataforma.
En el fichero del proyecto (de C# o de VB) busca <TargetFramework> y cambia net5.0 por net5.0-windows, ya que System.Management solo se puede usar en el sistema operativo Windows.

 

Nota:
Comentarte que el código (de C#) está basado en dos ejemplos encontrados en la red y de esos dos códigos de ejemplo he sacado el código del método GetUsbDriveLetters.
Estos son los enlaces que he usado:
El primero es prácticamente el usado en ese código, pero añadiendo la comprobación de que MediaType también pueda ser External, que saqué del segundo.
https://stackoverflow.com/a/31560283/14338047
https://stackoverflow.com/a/10018438/14338047

 

Código de C# de la utilidad para saber las unidades externas

public class USBUtil
{
    /// <summary>
    /// Comprueba si es una unidad externa.
    /// </summary>
    /// <param name="elPath">Path completo del que se extraerá la letra de unidad.</param>
    /// <returns></returns>
    public static bool EsUnidadExterna(string elPath)
    {
        var disco = Path.GetPathRoot(Path.GetFullPath(elPath));
        var usbD = GetUsbDriveLetters();
        return usbD.Contains(disco);
    }

    // Código basado en dos ejemplos de "la red".
    // https://stackoverflow.com/a/31560283/14338047
    // https://stackoverflow.com/a/10018438/14338047

    /// <summary>
    /// Las letras de las unidades externas conectadas por USB.
    /// </summary>
    /// <returns></returns>
    public static List<string> GetUsbDriveLetters()
    {
        // Hay que usar External para que también tenga en cuenta los USB de tipo fijo.
        var usbDrivesLetters = from drive in new ManagementObjectSearcher("select * from Win32_DiskDrive WHERE MediaType like '%External%' OR InterfaceType='USB'").Get().Cast<ManagementObject>()
                               from o in drive.GetRelated("Win32_DiskPartition").Cast<ManagementObject>()
                               from i in o.GetRelated("Win32_LogicalDisk").Cast<ManagementObject>()
                               select string.Format("{0}\\", i["Name"]);

        return usbDrivesLetters.ToList();
    }

}

 

Código de Visual Basic de la utilidad para saber las unidades externas

Public Class USBUtil

    ''' <summary>
    ''' Comprueba si es una unidad externa.
    ''' </summary>
    ''' <param name="elPath">Path completo del que se extraerá la letra de unidad.</param>
    ''' <returns></returns>
    Public Shared Function EsUnidadExterna(elPath As String) As Boolean
        Dim disco = Path.GetPathRoot(Path.GetFullPath(elPath))
        Dim usbD = GetUsbDriveLetters()
        Return usbD.Contains(disco)
    End Function

    ''' <summary>
    ''' Las letras de las unidades externas conectadas por USB.
    ''' </summary>
    ''' <returns></returns>
    Public Shared Function GetUsbDriveLetters() As List(Of String)
        ' Hay que usar External para que también tenga en cuenta los USB de tipo fijo.
        Dim usbDrivesLetters = From drive In New ManagementObjectSearcher("select * from Win32_DiskDrive WHERE MediaType like '%External%' OR InterfaceType='USB'").[Get]().Cast(Of ManagementObject)()
                               From o In drive.GetRelated("Win32_DiskPartition").Cast(Of ManagementObject)()
                               From i In o.GetRelated("Win32_LogicalDisk").Cast(Of ManagementObject)()
                               Select String.Format("{0}\", i("Name"))
        Return usbDrivesLetters.ToList()
    End Function

 

Nota:
Debes añadir importación de los espacios de nombres:
System
System.Collections.Generic
System.IO
System.Linq
System.Management (de NuGet)

 

Código de ejemplo usar estas los métodos de USBUtil.

class Program
{
    static void Main(string[] args)
    {
        Console.Title = "Ejemplo de Visual C# usando .NET 5.0 (net5.0-windows) y System.Management Version=5.0.0";

        Console.WriteLine("Mostrar las unidades usando DriveInfo.GetDrives().");
        foreach (var dr in DriveInfo.GetDrives())
            Console.WriteLine("Unidad: {0}, tipo: {1}", dr.Name, dr.DriveType);

        Console.WriteLine();

        Console.WriteLine("Mostrar las unidades extraíbles.");
        var usb = USBUtil.GetUsbDriveLetters();
        foreach (var s in usb)
            Console.WriteLine("{0}", s);

        Console.WriteLine();
        Console.ReadKey();
    }

Module Program
    Sub Main(args As String())
        Console.Title = "Ejemplo de Visual Basic usando .NET 5.0 (net5.0-windows) y System.Management Version=5.0.0"

        Console.WriteLine("Mostrar las unidades usando DriveInfo.GetDrives().")
        For Each dr In DriveInfo.GetDrives()
            Console.WriteLine("Unidad: {0}, tipo: {1}", dr.Name, dr.DriveType)
        Next
        Console.WriteLine()

        Console.WriteLine("Mostrar las unidades extraíbles.")
        Dim usb = USBUtil.GetUsbDriveLetters()

        For Each s In usb
            Console.WriteLine("{0}", s)
        Next

        Console.WriteLine()
        Console.ReadLine()
    End Sub
End Module

Y esto es todo, espero te resulte de utilidad 😉

Nos vemos.
Guillermo

Lo que estoy maquinando sobre expresiones lambda (métodos anónimos), programación asíncrona (tareas en otros hilos), cancelación de tareas, Parallel.For, consultas LINQ, DataGridView virtual y más, tanto para C# como para Visual Basic

Pues eso… ¡peasso de título! y seguramente se me olvidará algo, por eso he puesto al final «y más». Pero la idea que tengo en mente hoy 24 de febrero de 2022 (con la guerra de Ucrania fresquita, aunque es algo que, lamentablemente no es tan fresca y que ya viene de largo), es explicarte en una serie de posts o entradas en el blog todo lo indicado en el título.

Sobre las expresiones lambda o métodos anónimos, he pensado en ponerte un par de videos que Héctor de León publicó a principios del año pasado en su canal de YouTube (HDELEON.NET), pero como el código que él utiliza es para C# .NET, te mostraré también el equivalente para VB.NET así, si eres de los que utilizan C# lo puedas entender mejor. Todo esto contando con que él me autorice mostrar su código de C# y el equivalente (en la medida de lo posible) de Visual Basic para punto NET.
Actualización de las 17:33: Autorización que ya me ha dado 😉 ¡Gracias Héctor!

En cuanto a la programación asíncrona, te explicaré cómo crear tareas en otros hilos (principalmente con Task.Run) y el uso de async y await. Esas tareas se lanzarán o se procesarán también en el hilo principal de una aplicación de tipo Windows Forms (todo el código creado para .NET Core versiones 5 y/o 6) de forma que sepas qué cosas debes saber para el uso de controles, etc. entre hilos, cosa que solucionaré con InvokeRequired y la llamada a un delegado mediante Invoke. Por supuesto verás cómo definir delegados y cómo usarlos en el código.
Con todo esto verás cómo acceder a parte del código que se ejecuta en el hilo principal (el creado para mostrar el formulario de inicio) desde otro hilo (o tarea).

También verás cómo cancelar esas tareas y cómo controlarlas, todo ellos mediante el uso de objetos CancellationTokenSource y CancellationToken (tanto en su forma normal y anulable). Y lo que debes hacer para comprobar en el código cuando se ha cancelado y cómo tratar esa cancelación, algo que harás pasándole a la tarea (Task) el objeto de tipo CancellationToken.

Y como algunas de las tareas (ya sean asíncronas o no) puede que tenga que acceder a objetos de una colección, verás algunos casos de cómo usar Parallel.For para repartir en tareas el proceso de comprobación del contenido de esa colección.

La mayoría de las cosas que se hará en el código de ejemplo requerirá de las expresiones lambda (o métodos anónimos) y su uso en expresiones de LINQ. Ya sabes: Where, Any, Select, etc.

Por último, parte del código de ejemplo lo haré usando un control DataGridView y en ese caso te mostraré cómo crearlo para usar una caché con los datos que manejará y todo ello usando el modo virtual de ese control, de esa forma, al menos en mi caso, he logrado agilizar (sobre todo acelerar) mostrar los datos en ese grid (o cuadrícula). La caché usada estará preparada para el tipo de datos que voy a usar en ese ejemplo.

Todo esto, lo iré publicando poco a poco, entre otras cosas, porque lo estoy usando en una aplicación que utiliza datos o tipos muy concretos. Si te sirve de algo, es una aplicación que estoy migrando de MS-DOS a Windows, y la mayoría de los datos los obtiene de ficheros de texto… ¡Sí, así de vieja es! 🙂
Pero haré que el código acceda a colecciones de datos más simples, que en principio no se obtendrá de una base de datos, pero no descarto que también haga alguna modificación para acceder a una base de datos, y si es remota, mejor. Pero eso ya lo veré en su momento.

Bueno, te dejo por ahora y ya iré poniendo cosas… seguramente pondré los enlaces en este mismo post, pero si se me olvida hacerlo… pues… ¡busca en fechas posteriores al 24 de febrero de 2022! 😉

Y ya sabes… si me quieres invitar a un cafelillo o refresco virtual, puedes usar el enlace de DONAR con PayPal. Gracias de antemano.

Nos vemos.
Guillermo

Obtener las IPs (IPv4 e IPv6) del usuario y mostrar solo las IPv4 usando .NET (Core) [Código para Visual Basic y C#]

Pues eso… aquí tienes un ejemplo que son 2 (o cuatro si tenemos en cuenta que el código que te voy a mostrar es tanto para Visual Basic como para C#/CSharp) y es para obtener las IPs del usuario (o donde se ejecute el código) y de esas IPs (que contendrá las IPv6 y las IPv4) extraer solo las IPv4.

Este código utiliza expresiones regulares (RegEx) y por tanto necesitas añadir una importación del espacio de nombres System.Text.RegularExpressions, también utiliza métodos para acceder al nombre del equipo (host name) y obtener las direcciones IP, por tanto, necesitarás una importación al espacio de nombres System.Net.

Este código sirve tanto para aplicaciones de escritorio (los ejemplos están hechos para aplicaciones de consola para todas las plataformas), aplicaciones móviles y aplicaciones WEB.

Yo lo he probado en las dos primeras, y cuando lo haga en la tercera, te mostraré el código usado (o lo pondré en el repositorio de GitHub que crearé para poner el código de ejemplo) y las cosas que debas tener en cuenta, siempre y cuando tengas algo que tener en cuenta 😉

Nota 01-sep-2021:
Ya he publicado una página ASPX (asp-net) para probar el código.
Para esa página he creado una DLL con idea de ponerla en el directorio BIN del hosting donde tengo alojado «mi sitio» www.elguille.info.
En GitHub también está el código de esa DLL (del proyecto, que en este caso lo he creado con VB).
Este es el enlace de la página de prueba: ComoNET: Utils IP (.NET Core) (elguille.info)

Nota:
Las dos funciones principales: la que obtiene las IPs y la otra que extrae las IPv4 de esas IPs están en una clase estática (compartida sería en VB, pero en este caso no es necesario indicar Shared) con métodos estáticos (así debe ser en C#), esos métodos en Visual Basic están declarados con Shared, mientras que en C# se utiliza static.

En realidad, en Visual Basic podría haber usado un módulo (Module) en vez de una clase y definir expresamente los métodos de forma compartida, pero la diferencia de declarar los métodos como compartidos en una clase y usar esos métodos desde un módulo es que «tras las bambalinas» hay más código si se define un módulo. Me refiero al código IL generado, aunque en realidad solo hay más código en el constructor del módulo, ya que el código IL de los métodos es el mismo en ambos casos.
Después te pongo algo sobre esto (seguramente en otro post).

Otro detalle es que en la medida de lo posible he usado declaraciones de variables explícitas en lugar de usar inferencia de tipos, para que así te resulte más fácil ver (o saber) de qué tipo de datos estoy hablando (o utilizando).

Obtener las direcciones IP del usuario actual

En esta primera parte del ejemplo, echaremos mano de los métodos GetHostName y GetHostAddresses de la clase Dns definida en System.Net.

El nombre del host es necesario para saber las IPs, ya que GetHostAddresses utiliza el nombre del host (¿anfitrión?), pero mejor ve el código y después ya veremos si hay algo que aclarar.

Empiezo con el de C# y después te muestro el de Visual Basic.

public static string ObtenerIPs()
{
    StringBuilder sb = new StringBuilder();
    string ipAddresses;

    try
    {
        var hostName = Dns.GetHostName();
        IPAddress[] addresses = Dns.GetHostAddresses(hostName);

        foreach (IPAddress address in addresses)
            sb.Append($"{address}, ");

        ipAddresses = sb.ToString().TrimEnd(", ".ToCharArray());
    }
    catch (Exception ex)
    {
        ipAddresses = "ERROR: " + ex.Message;
    }
    return ipAddresses;
}

 

Public Shared Function ObtenerIPs()

    Dim sb As New StringBuilder()
    Dim ipAddresses As String

    Try
        Dim hostName = Dns.GetHostName()
        Dim addresses As IPAddress() = Dns.GetHostAddresses(hostName)

        For Each address As IPAddress In addresses
            sb.Append($"{address}, ")
        Next

        ipAddresses = sb.ToString().TrimEnd(", ".ToCharArray())
    Catch ex As Exception
        ipAddresses = "ERROR: " & ex.Message
    End Try
    Return ipAddresses
End Function

 

Un par de cosas (tips) a destacar de ese código:
1- La variable addresses es un array del tipo IPAddress, para obtener cada una de las direcciones IP utilizo un bucle foreach (For Each) y las agrego al StringBuilder (por aquello de que consume menos recursos que si las concateno).
2- Como le voy añadiendo a cada IP una coma y un espacio al final (para separarlas), es necesario quitar esa coma del final, para ello utilizo TrimEnd() indicándole qué caracteres quiero quitar del final. Los caracteres indicados al método de la familia Trim deben ser caracteres, no una cadena, por eso utilizo una cadena con los caracteres a quitar del final y la convierto en array de tipo Char con ToCharArray.

Extraer las IPv4 de una cadena con otras cosas usando expresiones regulares

En el método que se encarga de hacer eso, utilizo la clase RegEx para mediante una cadena con «expresiones regulares» (que resultan ser buenas y no regulares 😉 ) y los casos en que ha tenido éxito, los voy agregando a un StringBuilder que después usaré (tras quitar lo que no haya que usar) para devolver como resultado de las direcciones IPv4 de esa cadena entrante.

El código regex para obtener las IPv4 lo he adaptado de un ejemplo del sitio programador clic: Expresión regular de dirección IP.

Aquí tienes el código de ese método para Visual Basic y C#.

Public Shared Function LasIPv4(ipAdresses As String) As String
    Dim sb As New StringBuilder()

    Dim sRegExIPv4 As String = "((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)"
    Dim r As Regex = New Regex(sRegExIPv4)

    For Each m As Match In r.Matches(ipAdresses)
        If m.Success Then
            sb.Append($"{m.Value}, ")
        End If
    Next

    Return sb.ToString().TrimEnd(", ".ToCharArray())
End Function

 

public static string LasIPv4(string ipAdresses)
{
    StringBuilder sb = new();

    string sRegExIPv4 = @"((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)";
    Regex r = new Regex(sRegExIPv4);
    foreach (Match m in r.Matches(ipAdresses))
    {
        if(m.Success)
        {
            sb.Append($"{m.Value}, ");
        }
    }

    return sb.ToString().TrimEnd(", ".ToCharArray());
}

Y esto es todo… ¡Ah! ¡No! que falta el código de cómo usar estos dos métodos.

Cómo usar los métodos anteriores

Eso está en el método Main del «programa» de consola.

Aquí tienes el método Main que los utiliza.

static void Main(string[] args)
{
    //Console.WriteLine("Hello World!");
    Console.WriteLine("Ejemplo en C# usando .NET 5.0 para obtener las IPs y extraer con RegEx solo las IPv4.");
    Console.WriteLine();

    string ipAdresses = Utils.ObtenerIPs();

    Console.WriteLine("La cadena con todas las IPs:");
    Console.WriteLine(ipAdresses);
    string ret = Utils.LasIPv4(ipAdresses);

    Console.WriteLine();
    Console.WriteLine("Direcciones IPv4:");
    Console.WriteLine(ret);

    Console.WriteLine();
    Console.WriteLine("Pulsa INTRO para terminar.");
    Console.ReadLine();
}

 

Sub Main(args As String())
    'Console.WriteLine("Hello World!")
    Console.WriteLine("Ejemplo en Visual Basic usando .NET 5.0 para obtener las IPs y extraer con RegEx solo las IPv4.")
    Console.WriteLine()

    Dim ipAdresses As String = Utils.ObtenerIPs()

    Console.WriteLine("La cadena con todas las IPs:")
    Console.WriteLine(ipAdresses)
    Dim ret As String = Utils.LasIPv4(ipAdresses)

    Console.WriteLine()
    Console.WriteLine("Direcciones IPv4:")
    Console.WriteLine(ret)

    Console.WriteLine()
    Console.WriteLine("Pulsa INTRO para terminar.")
    Console.ReadLine()
End Sub

 

Aquí te dejo una captura del programa en ejecución.

 

Ahora sí, esto ha sido todo amigos… 😉

Nos vemos.
Guillermo

P.S.
Aquí tienes el enlace al repositorio de GitHub con el código de ejemplo completo, tanto para Visual Basic como C# (con sus proyectos respectivos para usar con Visual Studio 2019 o superior, incluso puede que anterior, pero no lo he comprobado).
elGuille-info/Obtener-IP-y-extraer-IPv4: Ejemplo en C# y Visual Basic de cómo obtener las IP (IPv4 e IPv6) y extraer solo las IPv4 (github.com)