Colaboraciones en el Guille

Borrar temporales

[borrar archivos temporales de Windows y llevar la cuenta del número de archivos borrados]

 

Fecha: 04/Nov/2005 (3 de Noviembre de 2005)
Autor: Emilio Pérez Egido (Miliuco)

 


Aspectos principales.

Resumen.

Durante varios años he utilizado un script de Visual Basic DeleteTempFiles.vbs, creado en 1999 por Michael Harris y modificado posteriormente por Bill James y Marcial Carlés. Este script funciona correctamente, borrando los archivos temporales de Windows e informando al usuario del tamaño de los archivos borrados (dato que guarda en un archivo de texto en el directorio de Windows).

Pero ha llegado el momento de plantear: ¿por qué no en .NET? Y el resultado es esta aplicación, inspirada en DeleteTempFiles.vbs en cuanto a su función principal y también en cuanto al mantenimiento de información en un archivo de texto. En lugar de guardar el tamaño, se guarda el número acumulado de los archivos borrados.

En este ejercicio se propone una aplicación que borra los archivos temporales de Windows XP y la carpeta que los contiene, leyendo la variable de entorno TEMP, definida para el usuario de la sesión actual. Además, se muestra al usuario información sobre:

El programa detecta si es la primera vez que arranca (comprobando si existe el archivo de texto que guarda el número de archivos borrados). Si es la primera vez, el programa crea el archivo de texto, informa de ello al usuario, se cierra y reinicia (mediante la creación de un segundo hilo y la finalización del actual).

Código Visual Basic .NET.

Se importan los espacios de nombres necesarios para las distintas funcionalidades del programa:

'manejo de archivos y carpetas
Imports System.IO
'obtener variables de entorno
Imports System.Environment
'tratar cadenas de texto (ejemplo: codificación usada al escribir en un archivo de texto)
Imports System.Text
'proporciona clases e interfaces que permiten la programación multiproceso
Imports System.Threading
Se obtienen las rutas a los directorios Windows y Temp y al archivo de texto C:\Windows\Borrar_TEMP.txt con métodos de la clase Environment:
'ruta a la carpeta definida en la variable de entorno TEMP
Protected rutaTemp As String = Environment.GetEnvironmentVariable("TEMP")
'ruta a la carpeta de sistema (WINDIR --> directorio de Windows)
Protected rutaWindir As String = Environment.GetEnvironmentVariable("WINDIR")
'variable para la ruta completa al archivo de texto que registra el nº de archivos borrados
Protected rutaTxt As String = rutaWindir & "\Borrar_TEMP.txt"

Al cargar el formulario se configura la propiedad Form.CancelButton (obtiene o establece el control de botón que se activará cuando el usuario presione la tecla ESC) en el único botón de la ventana "OK (Esc)":

'al cargar el formulario
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'pulsar la tecla ESC equivale a pulsar el botón OK
        Me.CancelButton = Me.Button1
    End Sub

La primera vez que se ejecuta el programa, al comprobar que no existe el archivo de texto C:\Windows\Borrar_TEMP.txt, lo crea, informa al usuario de que el programa ha de reiniciarse para poder leer adecuadamente desde ese archivo, se cierra y vuelve a arrancar de forma automática; ésto se consigue creando un hilo (thread) y enlazándolo con el método nuevoForm() que arranca de nuevo la aplicación después de que ésta se haya cerrado en su hilo actual:

'comprobar si el archivo de registro existe (crearlo si no existe)
    Protected Sub archivoTxt()
        Dim archivo As New FileInfo(rutaTxt)
        'si "Borrar_TEMP.txt" no existe --> crearlo e informar al usuario para que reinicie
        If archivo.Exists = False Then
            archivo.CreateText()
            Dim aviso As String
            aviso = "Se ha creado el archivo ""Borrar_TEMP.txt"" en la carpeta de Windows." & vbCrLf & _
            "Es necesario reiniciar el programa para su correcto funcionamiento." & vbCrLf & _
            "Pulsa ""Aceptar"" y el programa se cerrará y reiniciará automáticamente."
            MessageBox.Show(aviso, "Reiniciar el programa", MessageBoxButtons.OK)
            '
            'cerrar el formulario y que se vuelva a abrir automáticamente para que en el nuevo inicio
            'lea correctamente desde el archivo Borrar_TEMP.txt y muestre la información
            '
            'crear un nuevo hilo (thread) que se enlaza con el método nuevoForm()
            'que arrancará otra instancia diferente de Form1 
            Dim hilo As New Thread(AddressOf nuevoForm)
            hilo.Start()
            'cerrar la aplicación y su hilo actual
            Application.Exit()
            '
            'si "Borrar_TEMP.txt" ya existe --> ejecutar el programa con normalidad
        Else
            'llamar a los métodos:
            '- leer el nº de archivos del directorio y
            '  el nº acumulado de archivos borrados en "Borrar_TEMP.txt"
            Call listadoPre()
            '- leer el nº de archivos borrados en la ejecucion actual y escribir en "Borrar_TEMP.txt"
            Call listadoPost()
        End If
    End Sub

    'método que inicia de nuevo la aplicación
    Public Shared Sub nuevoForm()
        'detenerse 0,2 segundos
        Thread.Sleep(200)
        'iniciar de nuevo la aplicación en el nuevo hilo
        Application.Run(New Form1)
    End Sub

El método listadoPre():

  1. cuenta los archivos y carpetas del directorio TEMP antes del borrado. Para ello utiliza clases que permiten enumerar archivos en directorios y subdirectorios y otras operaciones típicas como copiar, mover, cambiar de nombre, crear y eliminar directorios: DirectoryInfo y FileSystemInfo.
  2. También lee desde el archivo de texto el número acumulado de archivos borrados en las ejecuciones anteriores del programa, usando la clase Stream.Reader que permite leer archivos de texto secuenciales.
  3. Finalmente, elimina el directorio TEMP usando el método DirectoryInfo.Delete que elimina una instancia de DirectoryInfo especificando si se van a eliminar los subdirectorios y los archivos (True o False):
'método que cuenta los archivos y las carpetas de TEMP antes del borrado
    Protected Sub listadoPre()
        Try
            'variables para guardar el nº de archivos
            'iCuentaTempPre = nº de archivos en la raíz de TEMP
            'iCuentaSubdirPre = nº de carpetas en la raíz de TEMP
            'iCuentaSubdirFilesPre = nº de archivos en las carpetas de la raíz de TEMP
            Dim iCuentaTempPre, iCuentaSubdirPre, iCuentaSubdirFilesPre As Integer
            'la clase DirectoryInfo expone métodos de instancia para crear, mover y 
            'enumerar archivos en directorios y subdirectorios
            'se utiliza para operaciones típicas como copiar, mover, cambiar de nombre,
            'crear y eliminar directorios
            Dim dirTemp As New DirectoryInfo(rutaTemp)
            'comprobar si el directorio existe
            If dirTemp.Exists = True Then
                'contar los archivos de la raíz  de TEMP (no en subdirectorios)
                iCuentaTempPre = dirTemp.GetFiles.Length
                'contar todos los subdirectorios  de TEMP
                Dim dirSub As FileSystemInfo() = dirTemp.GetDirectories()
                iCuentaSubdirPre = dirSub.Length
                'contar todos los archivos de cada subdirectorio (sólo en el primer nivel de subdirectorios)
                Dim dirTemp2 As New DirectoryInfo(rutaTemp)
                For Each dirTemp2 In dirSub
                    iCuentaSubdirFilesPre += dirTemp2.GetFileSystemInfos.Length
                Next
                'mostrar información en la etiqueta de texto
                Me.Label1.Text = (iCuentaTempPre + iCuentaSubdirFilesPre).ToString
                'valor de t = nº total de archivos encontrados antes de borrar TEMP
                iCuentaTempAll = (iCuentaTempPre + iCuentaSubdirFilesPre)
                '
                'leer el nº de archivos borrados hasta la fecha desde un archivo de texto guardado en %WINDIR%
                'el archivo se llama C:\Windows\Borrar_TEMP.txt (siendo %WINDIR% = C:\Windows en este caso)
                'las clases Stream.Reader y Stream.Writer permiten tratar con archivos de texto secuenciales
                Dim srLector As StreamReader
                'variable para la primera línea del archivo de texto
                Dim sLinea As String
                '
                'leer la primera línea y pasarla a lCuentaBorrados
                srLector = New StreamReader(rutaTxt)
                sLinea = srLector.ReadLine
                lCuentaBorrados = CLng(sLinea)
                'cerrar el archivo
                srLector.Close()
                '
                'el método DirectoryInfo.Delete elimina una instancia de DirectoryInfo
                'especificando si se van a eliminar los subdirectorios y los archivos (True o False)
                'se asigna al directorio de archivos temporales
                dirTemp.Delete(True)
            Else
                'si no existe el directorio TEMP
                Me.Label1.Text = "0"
                Me.Label2.Text = "0"
            End If
            'capturar excepciones
        Catch pollo As Exception
            'pasar por alto la excepción si la causa es no poder borrar archivos por estar en uso (excepción System.IO.IOException)
            Dim sTipo As String
            sTipo = "System.IO.IOException"
            If Not pollo.GetType.ToString = sTipo Then
                MessageBox.Show(pollo.Message, "Aviso del programa", MessageBoxButtons.OK)
                Application.Exit()
            End If
        End Try
    End Sub

El método listadoPost():

  1. cuenta los archivos y carpetas del directorio TEMP después del borrado, estos archivos son los que no han podido ser borrados por estar en uso. Para ello utiliza clases que permiten enumerar archivos en directorios y subdirectorios y otras operaciones típicas como copiar, mover, cambiar de nombre, crear y eliminar directorios: DirectoryInfo y FileSystemInfo.
  2. También suma el número de archivos borrados hasta la fecha y el número de archivos borrados en la ejecución actual, y escribe este dato en el archivo de texto usando la clase Stream.Writer que permite escribir en archivos de texto secuenciales.
  3. Finalmente, crea el directorio temporal en caso de que no exista ya que algunos programas arrancan con errores si no detectan la presencia del directorio definido en TEMP. Por ejemplo: Visual Studio da un error del compilador, Windows Media Player no inicia correctamente, etc:
'método que cuenta los archivos  de TEMP después del borrado
    'estos archivos son los que no han podido ser borrados porque están en uso
    Protected Sub listadoPost()
        Try
            'variables para guardar el nº de archivos
            'iCuentaTempPost = nº de archivos en la raíz de TEMP
            'iCuentaSubdirPost = nº de carpetas en la raíz de TEMP
            'iCuentaSubdirFilesPost = nº de archivos en las carpetas de la raíz de TEMP
            Dim iCuentaTempPost, iCuentaSubdirPost, iCuentaSubdirFilesPost As Integer
            'la clase DirectoryInfo expone métodos de instancia para crear, mover y 
            'enumerar archivos en directorios y subdirectorios
            'se utiliza para operaciones típicas como copiar, mover, cambiar de nombre,
            'crear y eliminar directorios
            Dim dirTemp As New DirectoryInfo(rutaTemp)
            'comprobar si el directorio existe
            If dirTemp.Exists = True Then
                'contar los archivos de la raíz de TEMP (no en subdirectorios),
                iCuentaTempPost = dirTemp.GetFiles.Length
                '
                'ahora no hace falta contar los archivos de las subcarpetas pues el método
                'dirTemp.Delete(True) las ha eliminado
                'contar todos los subdirectorios de TEMP
                'Dim dirSub As FileSystemInfo() = dirTemp.GetDirectories()
                'iCuentaSubdirPost = dirSub.Length
                'contar todos los archivos de cada subdirectorio (sólo en el primer nivel de subdirectorios)
                'Dim dirTemp2 As New DirectoryInfo(rutaTemp)
                'For Each dirTemp2 In dirSub
                '    iCuentaSubdirFilesPost += dirTemp2.GetFileSystemInfos.Length
                'Next
                '
                'nº de archivos borrados hasta la fecha + nº de archivos borrados en la ejecución actual
                lCuentaBorrados = lCuentaBorrados + (iCuentaTempAll - iCuentaTempPost)
                '
                'escribir el nº de archivos en un archivo de texto guardado en %WINDIR%
                'el archivo se llama C:\Windows\Borrar_TEMP.txt (siendo %WINDIR% = C:\Windows en este caso)
                'las clases Stream.Reader y Stream.Writer permiten tratar con archivos de texto secuenciales
                Dim swEscritor As StreamWriter
                'variable para la primera línea del archivo de texto
                Dim sLinea As String
                ' StreamWriter recibe 3 parámetros:
                '- String con la ruta al archivo
                '- True para añadir texto al existente, False para sobreescribir el texto existente
                '- codificación de caracteres que se utilizará (default: página de códigos ANSI usada por el sistema)
                swEscritor = New StreamWriter(rutaTxt, False, Encoding.Default)
                sLinea = CStr(lCuentaBorrados)
                'escribir el valor de lCuentaBorrados en la primera línea
                swEscritor.WriteLine(sLinea)
                'cerrar el archivo
                swEscritor.Close()
                '
                'mostrar información en la etiqueta de texto
                'variable iCuentaTempAll --> total de archivos encontrados antes de borrar TEMP
                'restándole los archivos no borrados por estar en uso --> archivos borrados realmente
                Me.Label2.Text = (iCuentaTempAll - iCuentaTempPost).ToString
                'variable lCuentaBorrados --> nº de archivos borrados hasta la fecha + nº de archivos borrados en la ejecución actual
                If lCuentaBorrados = 0 Then
                    Me.Label3.Text = "Ningún archivo"
                Else
                    Me.Label3.Text = lCuentaBorrados.ToString("#,#") & " archivos"
                End If
            Else
                'crear el directorio temporal en caso de que no exista
                'algunos programas arrancan con errores si no detectan la presencia del directorio definido en TEMP
                'por ejemplo: Visual Studio da error de compilador, Media Player no inicia correctamente...
                dirTemp.Create()
                Me.Label1.Text = "0"
                Me.Label2.Text = "0"
            End If
            'capturar excepciones
        Catch pollo As Exception
            MessageBox.Show("AVISO: " & pollo.Message, "Aviso del programa", MessageBoxButtons.OK)
            Application.Exit()
        End Try
    End Sub

Existe una ayuda sencilla mediante un MessageBox con saltos de línea:

Private Sub PictureBox2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PictureBox2.Click
        Dim ayuda As String = "Borra los archivos temporales y la carpeta que los contiene" & vbCrLf & _
        "(variable de entorno TEMP, definida para el usuario actual)." & vbCrLf & vbCrLf & _
        "Muestra al usuario información sobre:" & vbCrLf & _
        "- Nº de archivos encontrados en la ejecución actual." & vbCrLf & _
        "- Nº de archivos borrados en la ejecución actual." & vbCrLf & _
        "- Nº total acumulado de archivos borrados por el programa" & vbCrLf & _
        " (leyendo este dato desde un archivo de texto en C:\WINDOWS)."
        MessageBox.Show(ayuda, "Acerca de...", MessageBoxButtons.OK)
    End Sub

Excepciones al borrar archivos.

Nos interesan dos tipos de excepción que pueden aparecer cuando al programa no le es posible borrar algún archivo, lo que suele suceder por que el archivo está en uso o porque no se puede aceder a él por algún otro motivo. Se trata de las excepciones  "System.IO.IOException" y "System.UnauthorizedAccessException"; es recomendable no capturar estas 2 excepciones en el método listadoPre() para no interrumpir el flujo del programa y evitar avisos de error:

'capturar excepciones
Catch pollo As Exception
'pasar por alto la excepción si la causa es no poder borrar archivos por estar en uso
' (excepción System.IO.IOException o System.UnauthorizedAccessException)
Dim sTipo1, sTipo2 As String
sTipo1 = "System.IO.IOException"
sTipo2 = "System.UnauthorizedAccessException"
If Not pollo.GetType.ToString = sTipo1 And Not pollo.GetType.ToString = sTipo2 Then
   MessageBox.Show(pollo.Message, "Aviso del programa", MessageBoxButtons.OK)
   Application.Exit()
End If
End Try

Imagen del programa en funcionamiento.

Imagen del programa Borrar TEMP


Espacios de nombres usados en el código de este artículo:

System.IO
System.Environment
System.Text
System.Threading


Fichero con el código de ejemplo: miliuco_borrartemp.zip - 143 KB


ir al índice principal del Guille