Archivo de la etiqueta: Visual Basic

Por fin se podrán ver los artículos / posts de elguillemola.com desde elguille.info

Pues eso, que desde que cambié el alojamiento de «mi sitio», elguille.info, de Axarnet/Domitienda a acens (de telefónica) los enlaces que tenía «incrustados» en las páginas (para que mostrase en una página de mi sitio el artículo de mi blog (este) no se mostraban a causa de un error que mostraba este texto:
The request was aborted: Could not create SSL/TLS secure channel
La solución ha sido fácil (después de haber buscado en la red sobre este error) y que he he econtrado en StackOverflow, concretamente en un comentario a una de las respuestas, no puedo poner el enlace a ese comentario, pero es el que tiene este texto:
You don’t need to exclusively set it to a single type, you can simply append as well. System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12;
– Nae Mar 20 ’18 at 8:44

Ese código es para C#, en Visual Basic sería este:



System.Net.ServicePointManager.SecurityProtocol = 
            System.Net.ServicePointManager.SecurityProtocol Or
               System.Net.SecurityProtocolType.Tls12



Lo que tengo que hacer ahora es ver si ese código simplemente lo puedo poner en la página maestra o tendré que ponerlo en cada página… creo que si se ejecuta una sola vez es suficiente, así que… si ves que da nuevamente ese error, te ruego que me lo digas. Gracias.

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)

Charla con Héctor de León: ¿Está Comenzando a Morir Visual Basic?

Pues eso… ayer día 30 de agosto estuve charlando con Héctor de León (del canal de YouTube hdeleon.net) y ya está publicada y aquí te dejo el enlace.

¿Está Comenzando a Morir Visual Basic? | Invitado: Guillermo Som del elguillemola.com

 

Espero que te resulte interesante.

 

Nos vemos.
Guillermo

Detectar por código C# o Visual Basic si una página ASP.NET se muestra en un móvil

Pues eso… aunque he leído por ahí que no es conveniente detectar si se está navegando en un dispositivo móvil… eso era porque lo hacía con código duro (hard-code) es decir, detectar según el valor devuelto por userAgent de window.navigator, que sí, que puede producir resultados no deseados… pero… si usas Request.Browser.IsMobileDevice la cosa cambia.

Y hacer esa comprobación es bien simple.
¿Dónde hacerla?
En cualquier parte del código «script» de tu página ASP.NET (de .NET Framework), ya sea que estés usando Visual Basic o C# (o incluso otros lenguajes que soporten las páginas web asp.net de .net framework).

Un par de ejemplos, por favor

Esta es una página .aspx con código para C#:

<%@ Page Language="C#" AutoEventWireup="true" %>

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Request.Browser.IsMobileDevice)
        {
            LabelDesktop.Visible = false;
            LabelMobile.Visible = true;
        }
        else
        {
            LabelDesktop.Visible = true;
            LabelMobile.Visible = false;
        }
    }
</script>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body style="font-family:Consolas">
    <form id="form1" runat="server">
        <div>
            <asp:Label runat="server" ID="LabelMobile"  Visible="false"
                Font-Size="xx-Large" Font-Bold="true"
                Text="Estas viendo esto en el navegador de un dispositivo móvil." />
            <asp:Label runat="server" ID="LabelDesktop"  Visible="false"
                Font-Size="xx-Large" Font-Bold="true"
                Text="Estas viendo esto en el navegador de escritorio." />
        </div>
    </form>
</body>
</html>

Esta es una página .aspx con código para Visual Basic:

<%@ Page Language="VB" AutoEventWireup="true"  %>

<script runat="server">
    Protected Sub Page_Load(sender As Object, e As EventArgs)
        If Request.Browser.IsMobileDevice Then
            LabelDesktop.Visible = False
            LabelMobile.Visible = True
        Else
            LabelDesktop.Visible = True
            LabelMobile.Visible = False
        End If
    End Sub
</script>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body style="font-family:Consolas">
    <form id="form1" runat="server">
        <div>
            <asp:Label runat="server" ID="LabelMobile"  Visible="false"
                Font-Size="xx-Large" Font-Bold="true"
                Text="Estas viendo esto en el navegador de un dispositivo móvil." />
            <asp:Label runat="server" ID="LabelDesktop"  Visible="false"
                Font-Size="xx-Large" Font-Bold="true"
                Text="Estas viendo esto en el navegador de escritorio." />
        </div>
    </form>
</body>
</html>

Nota:
Puedes tener las dos páginas en una misma aplicación o proyecto WEB de Visual Studio sin necesidad de hacer nada especial, solo tener las páginas en un sitio que acepte .NET Framework.

A tener en cuenta

Si muestras la páginas en un dispositivo móvil, es posible que el navegador te permita ver la página como «escritorio», en ese caso, el valor que devuelve es escritorio, no que es móvil.
En las siguientes capturas tienes la demostración.
En esas capturas estoy usando el Edge para Android en un Google Pixel 4a.

En la figura 1 estoy mostrando la página versión de Visual Basic en el móvil, que si quieres la puedes probar usando el enlace mostrado (está alojada en mi sitio: elguille.info/WebFormVB.aspx).

Figura 1.

 

En la figura 2 te muestro la versión para C# antes de cambiarla a modo escritorio.
También puedes probarla usando este enlace en mi sitio: elguille.info/WebFormCS.aspx.

Figura 2. Mostrando la página como móvil y queriendo cambiar a versión para ordenador

 

Figura 3. Mostrando la página como si estuviese en escritorio y queriendo cambiar a versión apra sitio móvil.

 

Nota:
También puedes probarlo en el navegador de escritorio usando las herramientas de desarrollador, que en Edge y Chrome se pueden acceder usando Ctrl+Shift+I.
Y desde esas herramientas puedes indicar que se muestre como si fuese en un móvil (ver la figura 4).

Figura 4. Usando las herramientas de desarrollador puedes ver en el escritorio cómo sería usarlo en un móvil… o casi.

Espero que te sea de utilidad 😉

Nos vemos.
Guillermo

Charlas (podcasts) con Luis del Valle Hernández de ProgramarFacil

Pues eso… esto es de hace unos años, del 4 de junio de 2015 concretamente (según me ha dicho Luis, porque yo ya no lo recordaba), y aunque la entrevista/charla se hizo de una vez, él las publicó (en modo podcast, audio) en dos partes.

Pero como las he buscado y no las he encontrado ni en mi sitio (elguille.info) ni en este blog, lo publico ahora aunque sea después de tanto tiempo ya que considero que deben estar «localizables» porque fueron las primeras :-).
Así podrás saber más cosas sobre mí… aunque a fecha de hoy ya hay 4 charlas en YouTube, es conveniente que estos dos podcasts también estén visibles al buscar las charlas o entrevistas que me han hecho.

Así que… aquí tienes los enlaces a estos dos podcasts con la entrevista de Luis del Valle Hernández que me hizo el 4 de junio de 2015:

Charla 1: Primera parte de la charla con El Guille programación .NET

Charla 2: Segunda parte de la charla con El Guille programación .NET

 

Y ya que estamos con los enlaces, te pongo también los 4 de YouTube que me han hecho entre junio de 2020 y abril de 2021 (en plena pandemia del COVID-19):

1- Entrevista a Guillermo Som (elGuille) por Sergio Alejandro Campos para el canal EXCELeINFO (16-jun-2020)

2- Pablo Tilotta entrevista a ElGuille (21-jul-2020)

3- La evolución de .Net, una plática de Héctor de León con «El Guille» (17-nov-2020)

4- SevillaDotNet: Café Virtual con ‘ElGuille’ (07-abr-2021)

 

Nos vemos.
Guillermo

Acompáñame en el café virtual con Marcelo este miércoles 7 de abril

Pues eso… Marcelo de SevillaDotNet me ha invitado a un café virtual el próximo miércoles día 7 de abril a las 18:30 hora de la España peninsular (una hora menos en Canarias), así que… si nos quieres acompañar regístrate y asiste siguiendo este enlace: Café Virtual con Guillermo Som.

Nota del martes 6 de abril
Si quieres, también puedes verlo en directo por YouTube o bien usar este mismo enlace para después de mañana 7 de abril de 2021:

SevillaDotNet: Café Virtual con ‘ElGuille’

 

 

Si quieres ver qué hora es ahora en España (Madrid) sigue el enlace y así sabrás qué hora será en tu país 😉
Me comentan que al pulsar el enlace te muestra la hora en tu ciudad.

Este es el resumen de la charla cafetera 🙂

En este café virtual vamos a charlar con Guillermo Som. Tras tantos años participando y ayudándonos a todos con su sitio elguille.info, probablemente te haya ayudado en algún momento.
Vamos a charlar sobre sus inicios en la informática, sobre el camino que él ha tenido cómo programador, de .NET y su evolución y mucho más.

¿Te unes a nosotros?

¡Te espero!

Nos vemos.
Guillermo

Mejorar el rendimiento al usar GetExecutingAssembly

Pues eso… ya te lo comenté hace unos meses, aunque estaba escondido en las novedades de gsNotasNET.Android v2.0.0.33, y era porque me daba error (sin avisar, para más señas) a la hora de usar la forma recomendada de usar Assembly.GetExecutingAssembly.

En esa ocasión me daba error la aplicación de Xamarin.Forms y sin saber porqué… Pero la he vuelto a probar y por ahora, al menos en el IDE de Visual Studio 2019 va bien.
De todas formas, ya sabes si usas lo que te voy a explicar aquí, y ves que la aplicación casca, ya sabes porqué es.

La documentación (en inglés) dice esto:

For performance reasons, you should call this method only when you do not know at design time what assembly is currently executing. The recommended way to retrieve an Assembly object that represents the current assembly is to use the Type.Assembly property of a type found in the assembly.

Que en el idioma actualizado de Cervantes viene a decir esto (según Google Translator):

Por motivos de rendimiento, debe llamar a este método solo cuando no sepa en tiempo de diseño qué ensamblado se está ejecutando actualmente. La forma recomendada de recuperar un objeto Ensamblado que representa el ensamblado actual es usar la propiedad Type.Assembly de un tipo que se encuentra en el ensamblado.

Aquí te voy a poner el código (tanto de Visual Basic como de C#) para usar esta forma recomendada de asignar un ensamblado (de la clase System.Reflection.Assembly).

Este código está en bibliotecas (proyectos del tipo Class Library) para usar con .NET Standard 2.0 (de esta forma los ensamblados se podrán usar tanto en .NET Core como en .NET Framework).

Ejemplo para C#

public static string VersionDLL()
{
    var ensamblado = typeof(AboutViewModel).Assembly;
    var fvi = FileVersionInfo.GetVersionInfo(ensamblado.Location);
    // FileDescription en realidad muestra (o eso parece) lo mismo de ProductName
    var s = $"{fvi.ProductName} v{fvi.ProductVersion} ({fvi.FileVersion})" + 
        $"\r\n{fvi.Comments}";

    return s;
}

Ejemplo para Visual Basic

Public Function VersionDLL() As String
    Dim ensamblado = GetType(DatosMostrar).Assembly
    Dim fvi = FileVersionInfo.GetVersionInfo(ensamblado.Location)
    ' FileDescription en realidad muestra (o eso parece) lo mismo de ProductName
    Dim s = $"{fvi.ProductName} v{fvi.ProductVersion} ({fvi.FileVersion})" &
        $"{vbCrLf}{fvi.Comments}"

    Return s
End Function

Ese código mostrará la versión del ensamblado, la versión del fichero y la descripción, aunque en realidad a la propiedad a la que tienes que acceder es a Comments, ya que FileDesciption muestra lo mismo que ProductName.

Y aquí tienes una captura para la aplicación de Android con los comentarios de las 3 bibliotecas que estoy usando, 2 de ellas escritas con Visual Basic y la tercera (la que le da funcionalidad visual a la aplicación está escrita en C#).

Figura 1. Captura en el emulador de Android.

Y esta otra captura es de la aplicación en el emulador (local) de UWP (Universal Windows Platform) pero con la aplicación real. Aunque en modo depuración.

Figura 2. Captura en el (emulador) de UWP.

Espero que te sea de utilidad… Esa es siempre la idea…

Nos vemos.
Guillermo

P.S.
El puñetero Jetpack agrega código (de más) a las imágenes y cuando estas se muestran desde www.elguille.info, simplemente no se ven… pero… puedes pulsar en el sitio (en blanco) en el que está la imagen y te la mostrará…
Lo mismo quito el Jetpack, que, aparte del «bloque» Markdown y el poder publicar en twitter y mi sitio de facebook, no me sirve de mucho…

Respuesta de Microsoft a la petición de seguir añadiendo características a Visual Basic

Esto es solo para que aparezca en las entradas recientes.

El comentario de hoy 8 de marzo de 2021 está en este enlace.

Por cierto, la petición ya no se puede votar, porque la han cerrado, pero se pueden añadir comentarios… 😉

#evolveVB #evolucionarVB

Nos vemos.
Guillermo

Nuevas ‘versiones’ de Visual Studio 2019

Pues eso… con fecha de anteayer 2 de marzo, hay nuevas versiones de Visual Studio 2019, tanto para la versión final: v16.9.0 como para la preview: v16.10 Preview 1.0.

Este es un extracto de las novedades de .NET Productivity de la versión 16.9.0:
He resaltado las que, a mí me, parecen más interesantes.

.NET Productivity (versión final v16.9.0)

  • There is now IntelliSense completion for preprocessor symbols.
  • Solution Explorer now displays the new .NET 5.0 Source Generators.
  • Go To All won’t display duplicate results across netcoreapp3.1 and netcoreapp2.0.
  • Quick Info now displays compiler warning IDs or numbers for suppressions.
  • Using directives will now automatically get added when copying and pasting types to a new file.
  • IntelliSense completion will now automatically insert a semicolon as a commit character for object creation and method completion.
  • Semantic colorization for C# 9.0 records.
  • Refactoring that removes unnecessary discards.
  • Refactoring that converts a verbatim and regular string to an interpolated string preserving curly braces that were intended to go in the output.
  • Code fix in Visual Basic that removes the shared keyword when you convert methods that are shared to a module.
  • A refactoring that suggests using new(…) in non-contentious scenarios
  • A code fix that removes redundant equality expressions for both C# and Visual Basic
  • The .NET Code Style (IDE) analyzers can now be enforced on build
  • The Syntax Visualizer shows the current foreground color for enhanced colors
  • A new tooltip when hovering over the diagnostic ID for pragma warnings
  • When you type the return key from within a comment the new line is now automatically commented out
  • Inline parameter name hints enhancements
  • .NET Core Debugging with WSL 2

Para la versión preview… pues… mejor lo miras en la página de información publicada por la gente de Microsoft.

Nos vemos.
Guillermo

Cambiar nombres ficheros (utilidad)

Pues eso… aquí tienes una utilidad para cambiar los nombres de los ficheros. No solo cambiar un texto existente en el nombre, si no que también se puede añadir (o quitar) un texto al principio o al final del nombre del fichero.

Ver el P.S. 3 con los cambios del 1-mar-2021

Los ficheros a procesar los puedes indicar según la extensión de los mismos (o usando un filtro con * y/o ?. En total se pueden procesar 4 directorios/filtros.

En la figura puedes ver la aplicación en modo de ejecución.

Funcionamiento de la aplicación

Como puedes ver en la figura, tiene 4 grupos de directorios/ficheros con idea de que puedas indicar varias extensiones o bien 4 directorios diferentes.

Cada directorio se puede o no procesar (el primero siempre se tendrá en cuenta), por supuesto, si no indicas el path ni la extensión no se procesarán (o así debería ser, pero en realidad no hago ningana comprobación al respecto, así que… para salir de dudas… ¡pruébalo!).

En la parte inferior tienes las opciones de qué hará la aplciación, teniendo estas opciones:

  • Añadir el texto indicado en Text 1 al principio.
  • Añadir el texto indicado en Text 1 al final.
  • Quitar el texto indicado en Text 1 del principio.
  • Quitar el texto indicado en Text 1 del final.
  • Cambiar lo que haya en Text 1 por lo que hay en Texto 2

Y para las dos opciones de añadir texto puedes indicar si no se hace el cambio en el caso de que dicho texto (el de Texto 1) ya está.

Cuando se selecciona alguna de las dos opciones de añadir, se habilita ese control. En el resto de opciones está deshabilitado.

Lo mismo ocurre con el texto de Texto 2, que se habilitará solo si se selecciona la opción de cambiar, en las otras 4 opciones estará deshabilitado, y por tanto no se tendrá en cuenta lo que se escriba.

En las cajas de textos (Texto 1 y Texto 2) se pueden indicar espacios al final (y se tendrán en cuenta en la búsqueda y/o reeemplazo) y como visiblemente es difícil de ver si hay uno o más espacios, en caso de que haya más de un espacio al final, el programa los quitará y dejará solo uno. Si no hay espacios al final del texto no se añadirá ninguno.

Nota:

Hay que tener en cuenta, sobre todo al reemplazar texto (opción Cambiar) que el texto indicado en Texto 1 se reemplazará por el que haya en Texto 2 en todo el nombre del fichero (sin tener en cuenta ni el path ni la extensión).
Por tanto, si buscas el y el nombre original es Inclemencias del tiempo y quieres cambiar ese el por la el rtexto resultandte será (en mayúsculas los cambios realizados): IncLAmencias dLA tiempo.

El que avisa…

Controles usados en la utilidad

Esta utilidad (o aplciación) usa 4 controles del tipo BackgroundWorker con idea de usar de forma fácil la operación asíncrona.

El que está enlazado con el primer grupo (el que siempre está activo) siempre se ejecuta. Pero los otros 3 solo si se marca la opción que está arriba de cada grupo.

La barra de progreso tendrá en cuenta todos los ficheros que se vayan a procesar.

Y para saber el total de ficheros a procesar, en cada evento DoWork de cada control BackgroundWorker se añade a una colección definida a nivel del formulario en la que se almacenará los nombres de los ficheros a procesar (teniendo en cuenta el directorio en el que se buscarán los ficheros con el filtro indicado.

Ese filtro se hace usando lo indicado en cada caja de texto etiquetada tras Filtro. Como se pueden usar los comodines * y ? podrás usar cosas como estas:

  1. *.txt para indicar todos los ficheros con la extensión .txt
  2. algo?.xls* esto buscará los ficheros que empiecen con algo y cualquier carácter a continuación y que tengan una extensión que empiece por .xls

Como el código está compilado con .NET Framewwork 4.8 en realidad esto se puede hacer sin indicar el asterisco final, es decir, se puede indicar de esta forma: algo?.xls para que acepte todas las extensiones que empiecen por xls (.xls y .xlsx).

En .NET 5.0 habría que hacerlo como he comentado en el segundo punto, pero no es este el caso de esta aplicación.

Algo de código fuente

El código fuente está publicado en GitHub Cambiar-Nombres-Ficheros-v1

Aquí te muestro el código del evento Click del botón Procesar:

Private Sub btnProcesar_Click(sender As Object, e As EventArgs) Handles btnProcesar.Click
    ficheros.Clear()

    ' Si al final del texto a buscar/poner
    ' hay más de un espacio, cambiarlo por solo 1
    If txtTexto1.Text.EndsWith(" ") Then
        txtTexto1.Text = txtTexto1.Text.TrimEnd(" "c) & " "
    End If
    If txtTexto2.Text.EndsWith(" ") Then
        txtTexto2.Text = txtTexto2.Text.TrimEnd(" "c) & " "
    End If

    If chkProces1.Checked Then
        bgwProc1.RunWorkerAsync()
    End If
    If chkProces2.Checked Then
        bgwProc2.RunWorkerAsync()
    End If
    If chkProces3.Checked Then
        bgwProc3.RunWorkerAsync()
    End If

    ' Este siempre se procesa
    ' Hacer esta llamada al final para que se restauren los valores
    ' al terminar el trabajo.
    bgwProc.RunWorkerAsync()
End Sub

En los eventos DoWork de cada BackgroundWorker se hace la llamada al método … con el siguiente código:

backgroundWorker_DoWork(txtDir, txtExtensiones.Text)

Donde txtDir y txtExtensiones se usarán los nombres correwspondientes a cada grupo, por ejemplo, en el grupo 1 (el cero es el que siempre se ejecuta) se hará la siguiente llamada:

backgroundWorker_DoWork(txtDir1, txtExtensiones1.Text)

El código de backgroundWorker_DoWork es el siguiente, en donde solo se obtienen los ficheros que corresponden a cada directorio y con el filtro indicado.

''' <summary>
''' Método usado por el método DoWork de los BackgroundWorkers.
''' </summary>
''' <param name="txtDir">El directorio a usar.</param>
''' <param name="filtro">El filtro de los ficheros a usar.</param>
Private Sub backgroundWorker_DoWork(txtDir As TextBox,
                                    filtro As String)
    ' Aquí se acumulan los ficheros en la colección
    ' y se procesarán todos al final.
    ' De esta forma sabemos cuántos ficheros se procesan en total.
    Dim fileEnum = System.IO.Directory.EnumerateFiles(txtDir.Text, filtro)
    ficheros.AddRange(fileEnum)

End Sub

Una vez que finaliza el trabajo de bgwProc se procesan los ficheros.
Esto último se hace en el evento RunWorkerCompleted de ese control BackgroundWorker (en un momento verás el código).
Ahí se harán los cambios de todos los ficheros (que estarán en la colección ficheros), llamando al método cambiarNombres al que le pasamos como argumento el nombre del fichero a procesar.
Una vez finalizado los cambios, (y para que de tiempo a ver el mensaje), lanzo un temporizador después de tres segundos en el que se limpiarán los mensajes y se ocultará la barra de progreso.

Cabe decir que si alguno de los otros procesos dura má que este último puede que no se procesen bien todos los ficheros, pero… eso es algo que podemos arreglar en una futura revisión de este programa.
Y que a mí se me ocurre que se puede hacer comprobando cuando terminan todos los procesos asíncronos (comprobando cada uno de los eventos RunWorkerCompleted de cada tarea).
Pero por ahora te lo dejo como tarea que debes hacer y que podrás comprobar el día que yo lo publique.

Este es el código del método que hace todo esto (y como tip decirte que ese código tendrá que estar en otro sitio diferente si quieres tener en cuenta lo que he comentado en la nota anterior).

Private Sub bgwProc_RunWorkerCompleted(sender As Object,
                                       e As System.ComponentModel.RunWorkerCompletedEventArgs) _
                                       Handles bgwProc.RunWorkerCompleted
    ' Procesar los ficheros acumulados en la colección
    ProgressBar1.Visible = True
    ProgressBar1.Maximum = ficheros.Count
    ProgressBar1.Value = 0

    For i = 0 To ficheros.Count - 1
        cambiarNombres(ficheros(i))
        ProgressBar1.Value = i + 1
    Next

    Dim s As String
    If ficheros.Count = 1 Then
        s = "el nombre al fichero."
    Else
        s = $"los {ficheros.Count} nombres."
    End If
    LabelStatus.Text = $"Finalizado el proceso de cambiar {s}"
    Application.DoEvents()

    ' Mostrar ese mensaje por 3 segundos
    Timer1.Interval = 3000
    Timer1.Enabled = True

End Sub

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    Timer1.Enabled = False

    ProgressBar1.Visible = False
    LabelStatus.Text = LabelStatus.Tag.ToString()
    Application.DoEvents()

End Sub

Por último te muestro el código del método cambiarNombres.

Private Sub cambiarNombres(file As String)
    Dim fi As New System.IO.FileInfo(file)
    Dim s As String = file ' fi.FullName

    LabelStatus.Text = $"{fi.Name}"
    Application.DoEvents()

    If optCambiar.Checked Then
        s = fi.Name.Replace(txtTexto1.Text, txtTexto2.Text)
        If s <> fi.Name Then
            My.Computer.FileSystem.RenameFile(file, s)
        End If
    ElseIf optAñadirPrincipio.Checked Then
        If chkNoDuplicar.Checked Then
            If fi.Name.StartsWith(txtTexto1.Text) = False Then
                s = txtTexto1.Text & fi.Name
                My.Computer.FileSystem.RenameFile(file, s)
            End If
        Else
            s = txtTexto1.Text & fi.Name
            My.Computer.FileSystem.RenameFile(file, s)
        End If
    ElseIf optAñadirFinal.Checked Then
        If chkNoDuplicar.Checked Then
            If fi.Name.EndsWith(txtTexto1.Text) = False Then
                s = fi.Name & txtTexto1.Text
                My.Computer.FileSystem.RenameFile(file, s)
            End If
        Else
            s = fi.Name & txtTexto1.Text
            My.Computer.FileSystem.RenameFile(file, s)
        End If
    ElseIf optQuitarPrincipio.Checked Then
        If fi.Name.StartsWith(txtTexto1.Text) Then
            s = fi.Name.Replace(txtTexto1.Text, "")
            My.Computer.FileSystem.RenameFile(file, s)
        End If
    ElseIf optQuitarFinal.Checked Then
        If fi.Name.EndsWith(txtTexto1.Text) Then
            s = fi.Name.Replace(txtTexto1.Text, "")
            My.Computer.FileSystem.RenameFile(file, s)
        End If
    End If

    LabelStatus.Text &= $" --> {s}"
    Application.DoEvents()
End Sub

Y esto es todo por ahora… espero que te sea de utilidad, y si quieres indicar alguna mejora o algún fallo, eres libre de hacerlo en los comentarios de este post.

Muchas gracias.

Nos vemos.
Guillermo

P.S.
Crearé la versión de C# de esta utilidad (por ahora solo la tengo en Visual Basic) y cuando la tenga finalizada, la publicaré también en GitHub.
Aunque esa versión de CSharp la haré con los cambios que antes te he indicado: para asegurar que todos los procesos asíncronos han finalizado.

P.S.2 (28-feb-2021 02:21)
Ya está el código modificado teniendo en cuenta que se finalicen todos los procesos de los BackgroundWorker y además con el código para C#.
Puedes ver el código fuente en GitHub.

Para hacer lo que comentaba más arriba: comprobar que todos los BackgroundWorkers han finalizado he modificado el código de forma que se hacen estas comprobaciones:

  1. Defino dos variables a nivel de formulario para saber cuántos grupos se van a procesar (cada grupo se corresponde con un BackgroundWorker) y cuántos han finalizado.
  2. En el evento RunWorkerCompleted se usa el siguiente código (te muestro el de C#):
private void bgwProc_RunWorkerCompleted(object sender, 
                                        System.ComponentModel.RunWorkerCompletedEventArgs e)
{
    cuantosFinalizados += 1;
    // Cuando estén todos los procesos finalizados, hacer los cambios.
    if (cuantosFinalizados == cuantosProcesos)
        finalizarCopia();
}
  1. El código que había antes en ese método de evento ahora está en el método finalizaCopia, tal como puedes ver en el siguiente código que es para C#.
private void finalizarCopia()
{
    // Procesar los ficheros acumulados en la colección
    ProgressBar1.Visible = true;
    ProgressBar1.Maximum = ficheros.Count;
    ProgressBar1.Value = 0;

    for (var i = 0; i <= ficheros.Count - 1; i++)
    {
        cambiarNombres(ficheros[i]);
        ProgressBar1.Value = i + 1;
    }

    string s;
    if (ficheros.Count == 1)
        s = "el nombre al fichero.";
    else
        s = $"los {ficheros.Count} nombres.";
    LabelStatus.Text = $"Finalizado el proceso de cambiar {s}";
    Application.DoEvents();

    // Mostrar ese mensaje por 3 segundos
    timer1.Interval = 3000;
    timer1.Enabled = true;
}
  1. En el método de evento Click del botón Procesar ahora se tiene en cuenta las dos nuevas variables para saber cuántos grupos hay que tener en cuenta.
private void btnProcesar_Click(object sender, EventArgs e)
{
    ficheros.Clear();
    cuantosFinalizados = 0;
    cuantosProcesos = 1;

    // Si al final del texto a buscar/poner
    // hay más de un espacio, cambiarlo por solo 1
    if (txtTexto1.Text.EndsWith(" "))
        txtTexto1.Text = txtTexto1.Text.TrimEnd(' ') + " ";
    if (txtTexto2.Text.EndsWith(" "))
        txtTexto2.Text = txtTexto2.Text.TrimEnd(' ') + " ";

    if (chkProces1.Checked)
    {
        cuantosProcesos += 1;
        bgwProc1.RunWorkerAsync();
    }
    if (chkProces2.Checked)
    {
        cuantosProcesos += 1;
        bgwProc2.RunWorkerAsync();
    }
    if (chkProces3.Checked)
    {
        cuantosProcesos += 1;
        bgwProc3.RunWorkerAsync();
    }

    // Este siempre se procesa
    // Hacer esta llamada al final para que se restauren los valores
    // al terminar el trabajo.
    bgwProc.RunWorkerAsync();
}

Y esto es todo…
En GitHub está el nuevo código para Visual Basic y el proyecto de C#.

 
 
P.S. 3 (01-mar-2021)
Actualizado el código de VB y C# para que tenga en cuenta un par de fallillos que había:
– al añadir/quitar de los nombres el texto indicado, ya que se usba fi.Name y ahí se incluye la extensión, y la extensión no se debe tener en cuenta al cambiar el nombre.
– al marcar/desmarcar las opciones de los grupos, que se deshabilitaba todo, ahora solo se habilitan/deshabilitan los controles que contiene, salvo el propio CheckBox.