Есть ли способ узнать, был ли удален файл, удаленный с помощью system()?

Я пытаюсь создать программу, которая удаляет список файлов один за другим, используя system(). Причина, по которой я использую system() вместо remove(), заключается в том, что remove() не поддерживает переменные среды.

Я пытался проверить возвращаемое значение, но мой код, похоже, всегда выводит, что он был удален, даже если это не так.

Код:

void fileLoop() {
    std::vector<std::string> fileList = { "\"%userprofile%\\test.txt\"" };
    while (!inter) {
        for (int count = 0; count <= 0; count++) {
            std::string moddedstring = "del /F " + fileList[count];
            DWORD checker = GetFileAttributes((LPCWSTR)fileList[count].c_str());
            if (checker == INVALID_FILE_ATTRIBUTES) {
                goto next;
            }
            else {
                system(moddedstring.c_str());
                MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
            }
            next:
            std::cout << "\n";
        }
        Sleep(500);
    }
}

Я подумал, что есть какой-то простой способ выяснить это. Я пока не нашел такого способа.

Я добавлю больше путей позже.

Обновлять:

Я пробовал использовать OpenProcessToken() с ExpandEnvironmentStringsForUserW() для добавления ENV.

Но он жалуется, что мой буфер имеет тип LPCSTR*, даже когда я установил его на LPCSTR

Спасибо!

связанный: stackoverflow.com/questions/22953027/… - единственный способ с del - это либо проанализировать его вывод и найти сообщение об ошибке (вы можете использовать, например, _popen или подобное вместо system), либо использовать что-то вроде GetFileAttributes ( или lstat), чтобы увидеть, существует ли файл.

Jason C 10.01.2023 03:45

PS хотя del не возвращает значимого статуса, вы все равно, вероятно, захотите убрать это echo $?, так как это может еще больше запутать коды возврата.

Jason C 10.01.2023 03:47

«Причина, по которой я использую system() вместо remove(), заключается в том, что remove() не поддерживает переменные среды». - Тогда почему бы вам не составить имя файла, самостоятельно просматривая переменные окружения?

ShadowRanger 10.01.2023 03:49

^^^ вы можете использовать ExpandEnvironmentStringsW (или *A)

Jason C 10.01.2023 03:50

@JasonC: Спасибо, я забыл, что в Windows это стало еще проще. :-) Я полагаю, что для этого конкретного случая GetUserProfileDirectory (вариант *W или *A) или одна из других функций userenv.h также применима.

ShadowRanger 10.01.2023 03:51

@JasonC Я пытался GetFileAttributes, но когда файл был найден, он не выдал мой Msgbox. Есть идеи, почему?

TheException 10.01.2023 03:55

Чтобы было ясно, причина, по которой я предлагаю это, заключается в том, что вызов через system ограничивает вашу способность обнаруживать ошибки и реагировать на них; код выхода не супер информативен, относительно кодов возврата, errno, GetLastError() и т.д.

ShadowRanger 10.01.2023 03:55

вы должны показать, что вы сделали. может быть хорошим новым вопросом.

Jason C 10.01.2023 03:56

Не используйте system, это зияющая дыра в вашей программе и позволяет удаленно выполнять код.

NathanOliver 10.01.2023 03:57

@ShadowRanger Мой ENV, который я хочу использовать, — это не переменная среды пути, а ENV %USERPROFILE%. Если вы знаете, как это сделать, пожалуйста, покажите мне.

TheException 10.01.2023 03:57

@JasonC Я удалил echo $? из своей основной программы

TheException 10.01.2023 03:59

@TheException: %USERPROFILE% - это то, как вы говорите: «посмотрите USERPROFILE из среды и замените им заполнитель». Упомянутая JasonC функция ExpandEnvironmentStrings может выполнять ту же работу; в документах есть примеры.

ShadowRanger 10.01.2023 04:16

@TheException лучший способ получить путь к профилю пользователя — использовать SHGetFolderPath(CSIDL_PROFILE) или SHGetKnownFolderPath(FOLDERID_Profile). Тем не менее, используйте DeleteFile() вместо system("del"). И (LPCWSTR)fileList[count].c_str() не будет работать, как вы думаете, отбросьте приведение типов и вместо этого используйте GetFileAttributesA(). Или просто не утруждайте себя проверкой атрибутов, безоговорочно вызывайте DeleteFile() и дайте ему сказать вам, удалось это или нет.

Remy Lebeau 10.01.2023 05:09

@RemyLebeau SHGetFolderPath() и SHGetKnownFolderPath() имеют гораздо больше параметров.

TheException 10.01.2023 05:17

@TheException Я знаю об этом, я не показывал вам фактические фрагменты кода, просто упоминал имена функций и соответствующие идентификаторы папок для использования с ними.

Remy Lebeau 10.01.2023 10:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
16
86
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Лучший способ получить путь к профилю пользователя — просто запросить ОС напрямую через SHGetFolderPath(CSIDL_PROFILE) или SHGetKnownFolderPath(FOLDERID_Profile).

Кроме того, вы должны использовать DeleteFileA() вместо system("del"). Нет необходимости запускать внешний консольный процесс, если вы можете выполнить удаление напрямую. Кроме того, потому что вы заинтересованы в проверке ошибок, что намного проще, если вы используете Win32 API напрямую.

Кроме того, (LPCWSTR)fileList[count].c_str() не будет работать, как вы думаете. Вы не можете преобразовать const char* в const wchar_t*, используя простое приведение типов. Отбросьте приведение типов и используйте вместо него GetFileAttributesA().

Или просто не утруждайте себя проверкой атрибутов. Вы можете безоговорочно вызвать DeleteFileA() и позволить ему сказать вам, действительно ли это удалось или не удалось.

С учетом сказанного попробуйте что-то еще вроде этого:

#include <shlobj.h>

std::string getUserProfilePath() {
    CHAR szPath[MAX_PATH];
    if (FAILED(SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, szPath))) {
        // error handling...
        throw ...;
    }
    int len = lstrlenA(szPath);
    szPath[len] = '\\';
    return std::string(szPath, len + 1);

    /*
    PWSTR pszPath;
    if (FAILED(SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pszPath))) {
        // error handling...
        throw ...;
    }
    int wlen = lstrlenW(pszPath);
    int len = WideCharToMultiByte(0, 0, pszPath, wlen, NULL, 0, NULL, NULL);
    if (len == 0) {
        // error handling...
        throw ...;
    }
    std::vector<CHAR> buffer(len + 1);
    len = WideCharToMultiByte(CP_ACP, 0, pszPath, wlen, buffer.data(), len, NULL, NULL);
    if (len == 0) {
        // error handling...
        throw ...;
    }
    buffer[len] = '\\';
    CoTaskMemFree(pszPath);
    return std::wstring(buffer.data(), buffer.size());
    */
}

void fileLoop() {
    std::vector<std::string> fileList = { getUserProfilePath() + "test.txt" };
    while (!inter) {
        for (size_t count = 0; count < fileList.size(); ++count) {
            if (DeleteFileA(fileList[count].c_str())) {
                MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
            } else {
                // error handling...
            }
            std::cout << "\n";
        }
        Sleep(500);
    }
}

В качестве альтернативы, используя вместо этого строки Unicode (которые вам действительно следует использовать при взаимодействии с файловой системой):

#include <shlobj.h>

std::wstring getUserProfilePath() {
    WCHAR szPath[MAX_PATH];
    if (FAILED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, szPath))) {
        // error handling...
        throw ...;
    }
    int len = lstrlenW(szPath);
    szPath[len] = '\\';
    return std::wstring(szPath, len + 1);

    /*
    PWSTR pszPath;
    if (FAILED(SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pszPath))) {
        // error handling...
        throw ...;
    }
    int len = lstrlenW(pszPath);
    std::wstring sPath(len + 1, '\0');
    std::copy(pszPath, pszPath + len, sPath.begin());
    sPath[len] = '\\';
    CoTaskMemFree(pszPath);
    return sPath;
    */
}

void fileLoop() {
    std::vector<std::wstring> fileList = { getUserProfilePath() + L"test.txt" };
    while (!inter) {
        for (size_t count = 0; count < fileList.size(); ++count) {
            if (DeleteFileW(fileList[count].c_str())) {
                MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
            } else {
                // error handling...
            }
            std::cout << "\n";
        }
        Sleep(500);
    }
}

Еще лучше, если вы используете C++17 или более позднюю версию, рассмотрите возможность использования вместо этого библиотеки <filesystem>:

#include <shlobj.h>
#include <filesystem>

using fs = std::filesystem;

fs::path getUserProfilePath() {
    WCHAR szPath[MAX_PATH];
    if (FAILED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, szPath))) {
        // error handling...
        throw ...;
    }
    return szPath;

    /*
    PWSTR pszPath;
    if (FAILED(SHGetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pszPath))) {
        // error handling...
        throw ...;
    }
    fs::path pth(pszPath);
    CoTaskMemFree(pszPath);
    return pth;
    */
}

void fileLoop() {
    std::vector<fs::path> fileList = { getUserProfilePath() / L"test.txt" };
    while (!inter) {
        for (auto &pth : fileList) {
            std::error_code ec;
            if (fs::remove(pth, ec)) {
                MessageBoxW(NULL, L"File found, successfully deleted", L"File Deleted", MB_OK | MB_ICONWARNING);
            } else {
                // error handling...
            }
            std::cout << "\n";
        }
        Sleep(500);
    }
}

Можно ли использовать remove()? Я также использовал f.good(), чтобы проверить, существует ли файл. Используя это и SHGetFolderPath, это сработало!

TheException 11.01.2023 23:13

«Можно ли использовать remove()?» - Почему бы не быть в порядке? В Windows он просто вызовет DeleteFile() для вас. Так что избавьтесь от посредников. «Я также использовал f.good(), чтобы проверить, существует ли файл» — в показанном коде нет f. Но в любом случае не стоит проверять существование файла перед его созданием/удалением. Это условие гонки TOCTOU. Пусть файловая система сделает это за вас. Просто создайте/удалите файл безоговорочно и позвольте ему потерпеть неудачу, если файл (уже|нет) существует(-и) соответственно.

Remy Lebeau 11.01.2023 23:42

Другие вопросы по теме

Похожие вопросы