Создайте файл с флагом FILE_FLAG_DELETE_ON_CLOSE
-
21-09-2019 - |
Вопрос
Прежде чем я опишу свою проблему, вот описание программы (IHExplorer.exe) Я пишу:
Это приложение на языке C ++.
Приложение IHExplorer должно выглядеть как можно более похоже на окно проводника Windows.За одним исключением, и оно заключается в том, что при запуске файлов из этого окна Проводника они сначала будут переведены во временную папку пользователя, затем запустится приложение, связанное с расширением файла, и файл будет удален при закрытии.
Проблема, с которой я сталкиваюсь, связана с автоматическим удалением при закрытии файла.Вот такой сценарий:
- Пользователь дважды щелкает зашифрованный текстовый файл в IHExplorer.
IHExplorer расшифровывает текстовый файл в памяти, затем записывает его в %TEMP% с помощью ::CreateFile, который возвращает дескриптор файла (IHExplorer должен держать этот дескриптор открытым по крайней мере до тех пор, пока не будет выполнен файл .txt в командной строке).
Оболочка IHExplorer запускает текстовый файл (вызывая ::ShellExecute) из его временного расположения.
- Теперь у IHExplorer и notepad есть дескриптор открытого файла.
- Файл должен быть автоматически удален, когда и IHExplorer, и notepad закрыли свой дескриптор файла, даже если IHExplorer закроется первым.
ОК.это базовый пользовательский пример, который описывает то, что я хочу, чтобы произошло.Проблема, с которой я сталкиваюсь, заключается в том, что когда я :: ShellExecute(), блокнот сообщает "Процесс не может получить доступ к файлу, потому что он используется другим процессом". (это был бы IHExplorer).Мне нужно обойти это и заставить notepad открыть его, даже если у меня все еще открыт дескриптор в IHExplorer.
Вот как выглядит мой вызов ::CreateFile:
DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE;
HANDLE hFile = ::CreateFile(strTempFile.c_str(), GENERIC_WRITE, dwShareMode, &sa, CREATE_NEW, dwFlagsAndAttributes, NULL);
Обратите внимание, что я использовал FILE_SHARE_DELETE, чтобы другие процессы (такие как notepad) могли открыть файл с доступом на удаление.
Обратите внимание, что я использовал атрибуты FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, чтобы указать, что файл является временным и должен быть удален при закрытии.
Также обратите внимание на параметр &sa.Это структура SECURITY_ATTRIBUTES, которую я использую, и я чувствую (надеюсь), что именно в этом заключается моя проблема.Вот снова код, на этот раз я опубликую всю функцию целиком, чтобы вы могли видеть, как я заполняю структуру SECURITY_ATTRIBUTES:
int CIHExplorerDoc::OpenFile(std::string strFileName, bool bIsFullPath) {
std::string strFullFilePath;
if(bIsFullPath) {
strFullFilePath = strFileName;
strFileName = IHawk::RemovePath(strFileName);
}else {
strFullFilePath = m_strDirectory + strFileName;
}
if(!HasEncryptionFileExtension(strFullFilePath)) {
LaunchFile(strFullFilePath);
}else {
//it's an encrypted file, so open it and copy unencrypted file to temp.
IHawk::EncryptedFileHandle hEncryptedFile(strFullFilePath.c_str(), true, theApp.GetKeyServer());
if(hEncryptedFile.IsValid()) {
std::string strTempFile = g_strTempFolder + IHawk::ChangeFileExtension(strFileName, "");
//TODO: Determine what the LPSECURITY_ATTRIBUTES should be.
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;
if(!InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION)) {
DWORD dwLastError = ::GetLastError();
LOG4CPLUS_ERROR(m_Logger, "Cannot launch file '" << strFullFilePath << "'. Failed to initialize security descriptor. GetLastError=" << dwLastError);
return dwLastError;
}
if(!SetSecurityDescriptorDacl(
&sd, // A pointer to the SECURITY_DESCRIPTOR structure to which the function adds the DACL
TRUE, // presence of a DACL in the security descriptor
NULL, // allows all access to the object
FALSE // DACL has been explicitly specified by a user
))
{
DWORD dwLastError = ::GetLastError();
LOG4CPLUS_ERROR(m_Logger, "Cannot launch file '" << strFullFilePath << "'. Failed to set security descriptor DACL. GetLastError=" << dwLastError);
return dwLastError;
}
if(!SetSecurityDescriptorGroup(
&sd, // A pointer to the SECURITY_DESCRIPTOR structure whose primary group is set by this function
NULL, // no primary group
FALSE // Indicates whether the primary group information was derived from a default mechanism
))
{
DWORD dwLastError = ::GetLastError();
LOG4CPLUS_ERROR(m_Logger, "Cannot launch file '" << strFullFilePath << "'. Failed to set security descriptor primary group. GetLastError=" << dwLastError);
return dwLastError;
}
if(!SetSecurityDescriptorOwner(
&sd, // A pointer to the SECURITY_DESCRIPTOR structure whose owner is set by this function.
NULL, // If this parameter is NULL, the function clears the security descriptor's owner information. This marks the security descriptor as having no owner.
FALSE // Indicates whether the owner information is derived from a default mechanism.
))
{
DWORD dwLastError = ::GetLastError();
LOG4CPLUS_ERROR(m_Logger, "Cannot launch file '" << strFullFilePath << "'. Failed to set security descriptor owner information. GetLastError=" << dwLastError);
return dwLastError;
}
if(!SetSecurityDescriptorSacl(
&sd, // A pointer to the SECURITY_DESCRIPTOR structure to which the function adds the SACL
FALSE, // the security descriptor does not contain a SACL
NULL, // security descriptor has a NULL SACL
FALSE // A pointer to a flag that is set to the value of the SE_SACL_DEFAULTED flag in the SECURITY_DESCRIPTOR_CONTROL structure if a SACL exists for the security descriptor
))
{
DWORD dwLastError = ::GetLastError();
LOG4CPLUS_ERROR(m_Logger, "Cannot launch file '" << strFullFilePath << "'. Failed to set security descriptor SACL. GetLastError=" << dwLastError);
return dwLastError;
}
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = TRUE;
DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
// DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE;
HANDLE hFile = ::CreateFile(strTempFile.c_str(), GENERIC_WRITE, dwShareMode, &sa, CREATE_NEW, dwFlagsAndAttributes, NULL);
//verify we created the file.
if(hFile == INVALID_HANDLE_VALUE) {
DWORD dwLastError = ::GetLastError();
return dwLastError;
}
//copy to temp
char buffer[64*1024];
size_t nBytesRead = hEncryptedFile.Read(buffer, sizeof(buffer));
while(nBytesRead) {
DWORD numBytesWritten;
if(!::WriteFile(hFile, buffer, nBytesRead, &numBytesWritten, (LPOVERLAPPED) NULL)) {
DWORD dwLastError = ::GetLastError();
LOG4CPLUS_ERROR(m_Logger, "Failed to write file to %TEMP% folder. GetLastError=" << dwLastError);
return dwLastError;
}
nBytesRead = hEncryptedFile.Read(buffer, sizeof(buffer));
}
hEncryptedFile.Close();
//execute the file from temp.
LaunchFile(strTempFile);
}
}
return 0;
}
Я думаю, что если я определю правильный SECURITY_DESCRIPTOR для передачи в ::CreateFile, он может работать так, как я хочу.Пожалуйста, помогите.
кстати, функция LaunchFile просто заканчивает вызовом ::ShellExecute для запуска файла.
Решение
Перечитав документ msdn, я боюсь, что ответил на свой собственный вопрос.FILE_FLAG_DELETE_ON_CLOSE Файл подлежит удалению немедленно после закрытия всех его дескрипторов, включая указанный дескриптор и любые другие открытые или дублирующиеся дескрипторы.Если для файла существуют открытые дескрипторы, вызов завершается с ошибкой, если только все они не были открыты в режиме общего доступа FILE_SHARE_DELETE.Последующие запросы на открытие файла завершаются неудачей, если не указан режим общего доступа FILE_SHARE_DELETE.В моем случае я сомневаюсь, что notepad запрашивает разрешение FILE_SHARE_DELETE, поэтому он не может открыть файл