Curso C++: Eliminar Carpetas y Archivos

Curso C++: Eliminar Carpetas y Archivos

Eliminar directorios completos en C++ puede ser un reto, especialmente cuando se busca hacerlo de manera segura y eficiente. En este capítulo, les mostraré un código que permite eliminar archivos o directorios, incluyendo subcarpetas y archivos contenidos. Veremos cómo funciona y algunas consideraciones importantes.

Ejemplo de Código para Eliminar Carpetas Completas

A continuación, presentamos un código en C++ que permite eliminar archivos y carpetas. Este código está estructurado para manejar tanto archivos individuales como directorios con contenido.

#if !defined(KILLFOLDER_H__)
#define KILLFOLDER_H__

// Includes necesarios
#include <rc_class/msgbox.h>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

// Definiciones de atributos de carpetas
#define FOLDER_NORMAL  (FILE_ATTRIBUTE_DIRECTORY)
#define FOLDER_WINSYS  (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN)
#define FOLDER_READONLY  (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY)
#define FOLDER_HIDDEN  (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN)
#define FOLDER_NOTMOVE  (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN)
#define FOLDER_COMPRESSED (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_COMPRESSED)
#define FOLDER_TEMP  (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_TEMPORARY)

// Mensajes de error
#define MSG_CAPTION   "Ha ocurrido un error"
#define MSG_ERROR_HANDLE "La dirección no es válida: \n%s"
#define MSG_ERROR_FILE  "El archivo %s está siendo utilizado por una aplicación externa.\nNo se puede continuar."

// Propiedades
#define ALLFILES "\\*.*" // Atributo: todos los archivos.
#define ROOTBACK ".."  // Carpeta superior.

class CKillFolder
{
private:
    HWND m_hWnd;
public:
    int Run(char*);
    CKillFolder(HWND hWndParent) { m_hWnd = hWndParent; };
};

int CKillFolder::Run(char* lpszInitPath)
{
    HANDLE hFile;
    BOOL bNextFile = true;
    WIN32_FIND_DATA tdataFind;
    string lpszTemp;
    vector<string> lpszDirectory;
    DWORD dwType;
    INT iPos;

    // Bloque A: Detecta si es archivo o carpeta
    dwType = GetFileAttributes(lpszInitPath);
    if (dwType != (dwType | FILE_ATTRIBUTE_DIRECTORY))
    {
        // Quita el atributo del archivo
        SetFileAttributes(lpszInitPath, (DWORD)0);
        // Intenta eliminar el archivo
        if (!DeleteFile(lpszInitPath))
        {
            MsgOut(m_hWnd, MB_ICONEXCLAMATION, MSG_ERROR_FILE, lpszInitPath);
            return 0;
        }
        return 1;
    }

    // Bloque B: Inicia la búsqueda de subcarpetas
    lpszDirectory.push_back(lpszInitPath);
    lpszDirectory[0] = lpszDirectory[0] + ALLFILES;

    iPos = 0;
    while (iPos < lpszDirectory.size())
    {
        hFile = FindFirstFile((char*)lpszDirectory[iPos].c_str(), &tdataFind);
        if (hFile == INVALID_HANDLE_VALUE)
        {
            MsgOut(m_hWnd, MB_ICONEXCLAMATION, MSG_CAPTION, MSG_ERROR_HANDLE, lpszDirectory[iPos].c_str());
            FindClose(hFile);
            return 0;
        }
        while (bNextFile == TRUE)
        {
            bNextFile = FindNextFile(hFile, &tdataFind);
            if (bNextFile && strcmp(tdataFind.cFileName, ROOTBACK))
            {
                switch (tdataFind.dwFileAttributes)
                {
                    case FOLDER_NORMAL:
                    case FOLDER_HIDDEN:
                    case FOLDER_WINSYS:
                    case FOLDER_READONLY:
                    case FOLDER_COMPRESSED:
                    case FOLDER_TEMP:
                    case FOLDER_NOTMOVE:
                        lpszTemp = lpszDirectory[iPos].substr(0, lpszDirectory[iPos].length() - 3) + tdataFind.cFileName + ALLFILES;
                        lpszDirectory.push_back(lpszTemp);
                        break;
                    default:
                        // Bloque D: Elimina el archivo
                        lpszTemp = lpszDirectory[iPos].substr(0, lpszDirectory[iPos].length() - 3) + tdataFind.cFileName;
                        SetFileAttributes((LPCTSTR)lpszTemp.c_str(), (DWORD)0);
                        if (!DeleteFile((LPCTSTR)lpszTemp.c_str()))
                        {
                            MsgOut(m_hWnd, MB_ICONEXCLAMATION, MSG_ERROR_FILE, tdataFind.cFileName);
                            FindClose(hFile);
                            return 0;
                        }
                        break;
                }
            }
        }
        FindClose(hFile);
        bNextFile = true;
        iPos = iPos + 1;
    }

    // Bloque E: Elimina subcarpetas encontradas
    while (iPos > 0)
    {
        iPos = iPos - 1;
        lpszTemp = lpszDirectory[iPos].substr(0, lpszDirectory[iPos].length() - 4);
        RemoveDirectory((char*)lpszTemp.c_str());
    }
    return 1;
}

#endif // _KILLFOLDER_

Explicación del Código

Este código define una clase CKillFolder con el objetivo de eliminar un archivo o una carpeta con todo su contenido. Aquí se presentan las funciones principales y cómo trabajan:

  1. Detección de Archivos y Carpetas: Utilizando GetFileAttributes, se identifica si la ruta proporcionada corresponde a un archivo o una carpeta.

  2. Eliminación de Archivos: Los archivos se eliminan después de cambiar sus atributos para que no sean de solo lectura. Si el archivo está en uso, muestra un mensaje de error.

  3. Búsqueda y Eliminación de Subcarpetas: Utiliza FindFirstFile y FindNextFile para recorrer las subcarpetas y almacenar sus rutas en un vector. Los archivos dentro de las carpetas se eliminan primero, y luego se elimina la carpeta.

Consideraciones Importantes

  • Archivos en Uso: El código no puede eliminar archivos que estén siendo utilizados por otra aplicación. Esto es importante, ya que evita errores críticos.

  • Manejo de Errores: Se muestra un cuadro de diálogo si algo falla durante la eliminación, lo cual es útil para depurar problemas comunes.

  • Eficiencia: Aunque este método es eficaz para eliminar archivos y carpetas, no es necesariamente el más rápido, especialmente cuando hay muchas subcarpetas y archivos.

Mejoras Potenciales

  • Manejo de Excepciones: El código actual depende de retornos numéricos para manejar errores, una posible mejora sería usar excepciones para un manejo más robusto de errores.

  • Compatibilidad Multiplataforma: Actualmente, este código solo funciona en entornos Windows. Adaptarlo para ser multiplataforma podría ser una mejora significativa.

  • Paralelismo: Para mejorar la eficiencia, se podrían ejecutar las eliminaciones en paralelo usando std::thread o similar.

Conclusión

Eliminar archivos y directorios completos en C++ no es una tarea trivial, especialmente cuando queremos hacerlo de manera segura y eficiente. Este ejemplo proporciona una base sólida para manejar este proceso, pero siempre se pueden hacer mejoras para aumentar la robustez y eficiencia del código. Recuerda siempre probar bien el código en un entorno controlado antes de implementarlo en situaciones críticas.