Каков наилучший способ использования объекта HANDLE из WINAPI, когда я хочу написать свой код, учитывая принцип RAII в С++
Я пишу следующий код:
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;
}
Но я не уверен, как реализовать автоматический класс RAII для этого объекта.
Создайте класс умного указателя, деструктор которого вызывает CloseHandle().
Также обратите внимание, что, поскольку стандарт C++ 17 C++ имеет пространство имен файловой системы, которое может делать то, что делает ваш показанный код, но стандартным и простым способом C++ (например, с использованием итераторов и т. д.).
Кстати, delete handle;
? Только delete
то, что явно получено new
. Не делай delete
того, чего не делал new
.
Тоже const bool removeDir = NULL
? Прежде всего, NULL
— это макрос обратной совместимости C для нулевых указателей. Если вы хотите установить переменную bool
, используйте стандартные значения true
и false
.
@Someprogrammerdude Спасибо!! Вы можете написать пример умного указателя в этом случае?
Дубликат Реализация RAII на итерации папки
Можете ли вы исправить мой код своими комментариями? @ХансПассант
@OhadPorat Поиск в Интернете FindFirstFileW RAII
мог бы сэкономить вам время.
unique_ptr<> не имеет деструктора, вызывающего CloseHandle(). Вы должны сделать свой собственный класс. Если ссылка вам не поможет, возможно, оставьте это на полке на некоторое время.
Для этого WIL предоставляет unique_hfind.
Лучшее решение — сделать средство удаления, которое может вызывать FindClose
:
struct FindCloser {
typedef HANDLE pointer;
void operator()(HANDLE h) const {FindClose(h);}
};
И затем мы делаем typedef:
using FindHandle = std::unique_ptr<HANDLE, FindCloser>;
И тогда RAII в вашем коде тривиален:
FindHandle handle = FindFirstFileW(path_for_search.c_str(), ffd.get());
Вы, вероятно, также захотите что-то подобное для CloseHandle:
struct HandleCloser {
typedef HANDLE pointer;
void operator()(HANDLE h) const {CloseHandle(h);}
};
using RaiiHandle = std::unique_ptr<HANDLE, HandleCloser>;
Теоретически можно использовать std::unique_ptr<void,BOOL(WINAPI *)(HANDLE)>
, а затем передать CloseHandle
в качестве второго параметра конструктору, но это (A) неприятно, (B) больше и (C) медленнее. Структура FindCloser
не имеет состояния (занимает 0 байт в памяти) и легко встраивается. BOOL(WINAPI *)(HANDLE)
занимает дополнительные ~8 байт в каждом unique_ptr
, и компилятору сложнее доказать, что BOOL(WINAPI *)(HANDLE)
всегда будет указывать на FindClose
, поэтому его часто нельзя встроить.
Не может ли пользовательское средство удаления быть лямбдой? Я думаю, так будет аккуратнее.
@PaulSanders: Можно, но у вас должен быть std::unique_ptr<HANDLE, decltype(FindCloserLambda)>
, что немного странно, но работает. Однако лямбда должна быть объявлена и определена до создания указателя, поэтому в этом нет особого смысла.
Почему std::unique_ptr<void,BOOL(WINAPI *)(HANDLE)>
будет медленнее, чем (предположительно) пользовательское средство удаления, которое делает то же самое?
@IInspectable Я уточнил. Структура FindCloser
не имеет состояния (занимает 0 байт в памяти) и легко встраивается. BOOL(WINAPI *)(HANDLE)
занимает дополнительные ~8 байт в каждом unique_ptr
, и компилятору сложнее доказать, что BOOL(WINAPI *)(HANDLE)
всегда будет указывать на FindClose
, поэтому его часто нельзя встроить.
Для общего
HANDLE
, который должен быть переданCloseHandle
, вы все еще можете использовать стандартный интеллектуальный указатель, но с пользовательским средством удаления, которое вызываетCloseHandle
. Легко переводится наFindFirstFile
иFindClose
.