Colaboraciones en el Guille

Limpiar la papelera de Windows XP y Windows 2000

[Usando funciones de la API Win32: Shell32.dll]

 

Fecha 27/Ene/2006 (26 de Enero de 2006)
Autor: Miliuco (Emilio Pérez Egido)

 


Resumen

Visual Basic .NET puede acceder a funciones localizadas en bibliotecas de vínculos dinámicos (DLL). En este ejercicio se accede a funciones de Shell32.dll para comprobar si la papelera está vacía o no, en cuyo caso se eliminan los archivos que contiene, no para substituir a Windows que ya realiza esta misma tarea perfectamente sino para recordar cómo utilizar código no administrado (no gestionado por el CLR de .NET Framework) en Visual Basic .NET.
Aunque el CLR realiza un control automático del código administrado para que sea seguro (controla los recursos del sistema para que la aplicación se ejecute sin fallos), en numerosas ocasiones es necesario buscar funciones fuera de la biblioteca de clases de .NET Framework, sobre todo en librerías de la API Win32.

Introducción a la importación de plataforma

VB .NET puede acceder a funciones localizadas en bibliotecas de vínculos dinámicos (DLL): el código administrado (.NET) llama a funciones no administradas, entre ellas las que se localizan en la API de Win32, invocando a las funciones que son exportadas y calculando las referencias a sus argumentos (enteros, cadenas, matrices, estructuras, etc.) dentro de los límites de la interoperabilidad. Este proceso se llama invocación de plataforma (pinvoke).
La invocación de plataforma permite controlar una parte importante del sistema operativo llamando a funciones de la API de Win32 pero,además de la API de Win32, hay muchas otras API y archivos DLL disponibles para .NET a través de la invocación de plataforma.

Algunos de los archivos DLL de la API de Win32 que se utilizan con frecuencia:

Al declarar funciones importadas, hay que especificar un punto de entrada que identifique la ubicación de la función en el archivo DLL.
La clase DllImport y el método DllImportAttribute, que se utilizan para definir los métodos de invocación de plataforma utilizados para obtener acceso a las API no administradas, indican que una DLL no administrada expone un método con atributos como punto de entrada estático.
Muchas de estas funciones se declaran como Public Shared en Visual Basic.

La declaración de funciones importadas con DllImport es propia de .NET y es diferente a la forma de hacerlo en Visual Basic 6.
.NET acepta ambas construcciones pero, si se emplea la de Visual Basic 6, el compilador la convierte a DllImport.
Como ejemplo, podemos importar una función llamada SHQueryRecycleBin de Shell32.dll de estas 2 maneras (pero la primera es más adecuada en .NET):

<System.Runtime.InteropServices.DllImport("shell32.dll")> _
Public Shared Function SHQueryRecycleBin(ByVal pszRootPath As String, ByRef pSHQueryRBInfo As SHQUERYRBINFO) As Integer
End Function

Private Declare Auto Function SHQueryRecycleBin Lib "shell32.dll" (ByVal pszRootPath As String, ByRef pSHQueryRBInfo As SHQUERYRBINFO) As Integer

El espacio de nombres System.Runtime.InteropServices, al que pertenecen la clase DllImport y el método DllImportAttribute, proporciona una gran variedad de miembros que admiten la interoperabilidad COM y los servicios de invocación de plataforma.

Puedes ampliar información sobre este tema en http://www.elguille.info/NET/vb6anet/equivalenciasAPI.htm

SHQueryRecycleBin

Esta función de Shell32.dll devuelve información sobre el número de ítems que hay en la papelera y cuánto espacio ocupan. Declaramos la función mediante DllImport y también declaramos la estrucutura SHQUERYRBINFO cuyos miembros contienen el tamaño y el número de elementos encontrados por SHQueryRecycleBin:

<System.Runtime.InteropServices.DllImport("shell32.dll")> _
Public Shared Function SHQueryRecycleBin(ByVal pszRootPath As String, ByRef pSHQueryRBInfo As SHQUERYRBINFO) As Integer
End Function

'la estructura SHQUERYRBINFO contiene el tamaño y el nº de ítems
'encontrados por la función SHQueryRecycleBin
'- cbSize: bytes en la estructura
'- i64Size: entero de 64 bits con el tamaño de los ítems de la papelera
'- i64NumItems: entero de 64 bits con el número de ítems de la papelera
Public Structure SHQUERYRBINFO
Dim cbSize As Long
Dim i64Size As Long
Dim i64NumItems As Long
End Structure

Ejecución de la aplicación

Al cargar el formulario se crea un objeto de tipo SHQUERYRBINFO que contiene la información sobre la papelera. La longitud de este objeto (número de bytes en la estructura) se puede extraer de 2 maneras:

'información sobre la papelera
Dim infopapelera As SHQUERYRBINFO
'método 1
infopapelera.cbSize = Marshal.SizeOf(GetType(SHQUERYRBINFO))
'método 2
infopapelera.cbSize = Len(infopapelera)

Si se usa la clase Marshal, es necesario importar System.Runtime.InteropServices; Marshal proporciona una colección de métodos para asignar memoria no administrada, copiar bloques de memoria no administrados y convertir los tipos administrados en no administrados, así como otros métodos diversos que se utilizan al interactuar con código no administrado. Pero si se se emplea Len() en lugar de Marshal, no es necesario importar System.Runtime.InteropServices.

Cuando existe más de una unidad de disco, cada unidad tiene su propia papelera de reciclaje. Llamar a la función SHQueryRecycleBin con el parámetro "" (String vacío) informa sobre todas las papeleras. Llamarla con "C:\" informa solamente sobre la papelera de la unidad C.

En Windows ME y Windows 2000 hay un fallo documentado en la función SHQueryRecycleBin cuando se pasa un valor NULL o "" a pszRootPath y en estos sistemas es necesario recuperar información de cada papelera por separado, que es lo que haremos aquí, recorriendo una matriz formada por las unidades lógicas del sistema y obteniendo información de la papelera de cada unidad. Pero en Windows XP podríamos ahorrarnos este paso y llamar a SHQueryRecycleBin con el parámetro "":

Call SHQueryRecycleBin("", infopapelera) 'XP

en lugar de

For Each drive In drives
Call SHQueryRecycleBin(drive, infopapelera) 'Me y win2k
Next
El código del evento Load del formulario queda así:

'al cargar el formulario
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'formulario oculto
Me.Visible = False
'Me.WindowState = FormWindowState.Minimized
Try
'información sobre la papelera
Dim infopapelera As SHQUERYRBINFO
'infopapelera.cbSize = Marshal.SizeOf(GetType(SHQUERYRBINFO))
infopapelera.cbSize = Len(infopapelera)
'
'array de cadenas para contener los nombres de las unidades lógicas del sistema
Dim drive, drives() As String
'rellenar el array con las unidades lógicas del sistema
drives = System.Environment.GetLogicalDrives()
'almacena el tamaño de la papelera de cada unidad y el total
Dim l As Long
'recorrer las unidades lógicas de forma sucesiva
For Each drive In drives
'para comprobar la detección de unidades durante el desarrollo del ejercicio
'MsgBox("drive: " & drive)
Call SHQueryRecycleBin(drive, infopapelera)
       l += infopapelera.i64Size
Next
'para comprobar tamaño de l durante el desarrollo del ejercicio
'MsgBox("infopapelera.i64Size: " & l)
'
'si el tamaño que ocupan los ítems en la papelera es mayor que cero
'(si la papelera NO está vacía)
If l > 0 Then
'mostrar diálogo para aceptar o cancelar
Dim f2 As Form3
       f2 = New Form3
       f2.ShowDialog()
Else '(si la papelera SÍ está vacía)
'mostrar diálogo para informar
Dim f1 As Form2
       f1 = New Form2
       f1.ShowDialog()
End If
Catch pollo As Exception
     MsgBox("ERROR:." & pollo.Message, MsgBoxStyle.Critical, "Aviso")
Finally
'siempre cerrar el formulario al final
Me.Close()
End Try
End Sub

Confirmación de la acción

Si la papelera está vacía, un cuadro de diálogo informa de que no es necesaria su limpieza. Pero si tiene algún ítem, otro cuadro de diálogo informa de ello y pide confirmación antes de vaciarla.

Usamos la función SHEmptyRecycleBin que borra el contenido de la papelera de todo el sistema o de una unidad en particular, con argumentos:

<System.Runtime.InteropServices.DllImport("shell32.dll")> _
Public Shared Function SHEmptyRecycleBin(ByVal hWnd As Integer, ByVal pszRootPath As String, ByVal dwFlags As Integer) As Integer
End Function

'constantes para modificar aspectos de la función SHEmptyRecycleBin
Private Const SHERB_NOPROGRESSUI = &H2 'sin pedir confirmación
Private Const SHERB_NOCONFIRMATION = &H1 'sin mostrar progresión
Private Const SHERB_NOSOUND = &H4 'sin sonido de sistema
'
'vaciar la papelera
SHEmptyRecycleBin(Me.Handle.ToInt32, "", SHERB_NOCONFIRMATION) 'sin pedir confirmación
'SHEmptyRecycleBin(Me.Handle.ToInt32, "", SHERB_NOPROGRESSUI) 'sin mostrar progresión
'SHEmptyRecycleBin(Me.Handle.ToInt32, "", SHERB_NOSOUND) 'sin sonido de sistema

La función SHUpdateRecycleBinIcon refresca el icono de la papelera en el escritorio para que refleje el estado actual del sistema después de vaciar la papelera pero por regla general no es necesario utilizarla:

'vaciar la papelera
SHEmptyRecycleBin(Me.Handle.ToInt32, "", SHERB_NOCONFIRMATION) 'sin pedir confirmación
'SHEmptyRecycleBin(Me.Handle.ToInt32, "", SHERB_NOPROGRESSUI) 'sin mostrar progresión
'SHEmptyRecycleBin(Me.Handle.ToInt32, "", SHERB_NOSOUND) 'sin sonido de sistema
'
'refrescar el icono de la papelera (no parece que sea necesario)
SHUpdateRecycleBinIcon()

Información del autor en el ensamblado

Rellenando adecuadamente los campos del archivo AssemblyInfo.vb proporcionamos una mayor información sobre el programa:

<Assembly: AssemblyTitle("Limpiar la papelera")> 'Descripción (título de la ventana de propiedades)
<Assembly: AssemblyDescription("Limpia la papelera de Windows XP")> 'Comentarios
<Assembly: AssemblyCompany("La Robla")>  'Organización
<Assembly: AssemblyProduct("Programa que accede a funciones de API Win32")>  'Nombre del producto
<Assembly: AssemblyCopyright("Miliuco")> 'Copyright (título de la ventana de propiedades)
<Assembly: AssemblyTrademark("http:"//www.miliuco.net")> 'Marca registrada
<Assembly: CLSCompliant(True)>

Ejemplo: el ensamblado en vista de mosaico:

Imagen del programa en funcionamiento


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

System.Runtime.InteropServices


Fichero con el código de ejemplo: miliuco_papelera.zip - 150 KB

(MD5 checksum: [47906C6D9FBEBFA6BAC7F17E5C5432CA])


ir al índice principal del Guille