Я пытаюсь создать программу, которая удаляет список файлов один за другим, используя 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
Спасибо!
PS хотя del
не возвращает значимого статуса, вы все равно, вероятно, захотите убрать это echo $?
, так как это может еще больше запутать коды возврата.
«Причина, по которой я использую system()
вместо remove()
, заключается в том, что remove()
не поддерживает переменные среды». - Тогда почему бы вам не составить имя файла, самостоятельно просматривая переменные окружения?
^^^ вы можете использовать ExpandEnvironmentStringsW (или *A
)
@JasonC: Спасибо, я забыл, что в Windows это стало еще проще. :-) Я полагаю, что для этого конкретного случая GetUserProfileDirectory
(вариант *W
или *A
) или одна из других функций userenv.h также применима.
@JasonC Я пытался GetFileAttributes
, но когда файл был найден, он не выдал мой Msgbox. Есть идеи, почему?
Чтобы было ясно, причина, по которой я предлагаю это, заключается в том, что вызов через system
ограничивает вашу способность обнаруживать ошибки и реагировать на них; код выхода не супер информативен, относительно кодов возврата, errno
, GetLastError()
и т.д.
вы должны показать, что вы сделали. может быть хорошим новым вопросом.
Не используйте system
, это зияющая дыра в вашей программе и позволяет удаленно выполнять код.
@ShadowRanger Мой ENV, который я хочу использовать, — это не переменная среды пути, а ENV %USERPROFILE%
. Если вы знаете, как это сделать, пожалуйста, покажите мне.
@JasonC Я удалил echo $?
из своей основной программы
@TheException: %USERPROFILE%
- это то, как вы говорите: «посмотрите USERPROFILE
из среды и замените им заполнитель». Упомянутая JasonC функция ExpandEnvironmentStrings
может выполнять ту же работу; в документах есть примеры.
@TheException лучший способ получить путь к профилю пользователя — использовать SHGetFolderPath(CSIDL_PROFILE)
или SHGetKnownFolderPath(FOLDERID_Profile)
. Тем не менее, используйте DeleteFile()
вместо system("del")
. И (LPCWSTR)fileList[count].c_str()
не будет работать, как вы думаете, отбросьте приведение типов и вместо этого используйте GetFileAttributesA()
. Или просто не утруждайте себя проверкой атрибутов, безоговорочно вызывайте DeleteFile()
и дайте ему сказать вам, удалось это или нет.
@RemyLebeau SHGetFolderPath()
и SHGetKnownFolderPath()
имеют гораздо больше параметров.
@TheException Я знаю об этом, я не показывал вам фактические фрагменты кода, просто упоминал имена функций и соответствующие идентификаторы папок для использования с ними.
Нет причин не использовать файловые API , и количество параметров не должно быть недостатком. Или, по крайней мере, как предложил @JasonC, используйте ExpandEnvironmentStrings, что работает для меня.
Лучший способ получить путь к профилю пользователя — просто запросить ОС напрямую через 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
, это сработало!
«Можно ли использовать remove()
?» - Почему бы не быть в порядке? В Windows он просто вызовет DeleteFile()
для вас. Так что избавьтесь от посредников. «Я также использовал f.good()
, чтобы проверить, существует ли файл» — в показанном коде нет f
. Но в любом случае не стоит проверять существование файла перед его созданием/удалением. Это условие гонки TOCTOU. Пусть файловая система сделает это за вас. Просто создайте/удалите файл безоговорочно и позвольте ему потерпеть неудачу, если файл (уже|нет) существует(-и) соответственно.
связанный: stackoverflow.com/questions/22953027/… - единственный способ с
del
- это либо проанализировать его вывод и найти сообщение об ошибке (вы можете использовать, например,_popen
или подобное вместоsystem
), либо использовать что-то вродеGetFileAttributes
( илиlstat
), чтобы увидеть, существует ли файл.