c++windowswinapihandleraii

How to use handle in WINAPI when I implement my c++ code with RAII principle?


What is the best way for using HANDLE object from WINAPI when I want to write my code consider RAII principle in c++

I write the following code:

bool Uninstall(wstring folder, const bool removeDir = NULL)
{
    //init for run over file in dir by winapi
    unique_ptr<WIN32_FIND_DATA> ffd = make_unique<WIN32_FIND_DATA>();
    HANDLE handle;
    wstring path_for_search = folder + L"\\*"; // for include all the things in path when run over    the files or directories
    //List files
    handle = FindFirstFileW(path_for_search.c_str(), ffd.get());
    // run over files and directories by handle
    do {
        if (wcscmp(ffd->cFileName, L".") != 0 && wcscmp(ffd->cFileName, L"..") != 0) // pass the default '.' and ".." 
        {
            wstring file_or_dir_path = folder + L"\\" + ffd->cFileName; // get the full file or dir path
            if (GetFileAttributes(file_or_dir_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) // check if directory
            {
                Uninstall(file_or_dir_path, false); // recursive call for uninstall the content in the sub dir itself your text
            }
            else
            {
                DeleteFileW(file_or_dir_path.c_str()); // delete the file
            }
        }
    } while (FindNextFile(handle, ffd.get()));
    FindClose(handle);
    delete handle;
    //check if remove the original dir
    if (!removeDir)
    {
        RemoveDirectoryW(folder.c_str());
    }
    return true;
}

But I'm not sure How to implement automatic RAII class for this object.


Solution

  • The best solution is to make a deleter that can call FindClose:

    struct FindCloser {
      typedef HANDLE pointer;
      void operator()(HANDLE h) const {FindClose(h);}
    };
    

    And then we make a typedef:

    using FindHandle = std::unique_ptr<HANDLE, FindCloser>;
    

    And then RAII in your code is trivial:

    FindHandle handle = FindFirstFileW(path_for_search.c_str(), ffd.get());
    

    You'll probably also want similar for CloseHandle:

    struct HandleCloser {
        typedef HANDLE pointer;
        void operator()(HANDLE h) const {CloseHandle(h);}
    };
    using RaiiHandle = std::unique_ptr<HANDLE, HandleCloser>;
    

    In theory, one could use a std::unique_ptr<void,BOOL(WINAPI *)(HANDLE)> and then pass CloseHandle as a second parameter to the constructor, but that's (A) obnoxious, (B) bigger and (C) slower. The FindCloser struct is stateless (takes 0 bytes in memory), and is trivially inlinable. A BOOL(WINAPI *)(HANDLE) takes an extra ~8 bytes in each unique_ptr, and is harder for the compiler to prove that the BOOL(WINAPI *)(HANDLE) will always point at FindClose, so it often can't be inlined.