使用FILE_FLAG_DELETE_ON_CLOSE标志创建文件

时间:2009-10-17 19:41:48

标签: c++ filesystems temporary-files createfile

在我描述我的问题之前,这里是我正在编写的程序(IHExplorer.exe)的描述:

这是一个C ++应用程序。

IHExplorer应用程序看起来就像Windows资源管理器窗口一样。有一个例外,那就是从这个资源管理器窗口中启动文件会先将它们解密到用户的临时文件夹,然后启动与文件扩展名关联的应用程序并在关闭时删除该文件。

我遇到的问题是文件关闭时自动删除。这是一个场景:

  1. 用户双击IHExplorer中的加密.txt文件。
  2. IHExplorer解密内存中的.txt文件,然后使用:: CreateFile将其写入%TEMP%,该文件将HANDLE返回给文件(IHExplorer必须保持此句柄至少打开,直到.txt文件被shell执行)。

  3. IHExplorer Shell从它的临时位置执行.txt文件(通过调用:: ShellExecute)。

  4. 现在,IHExplorer和记事本都有一个打开文件的句柄。
  5. 当IHExplorer和记事本同时关闭文件句柄时,必须自动删除该文件,即使IHExplorer先关闭。
  6. 确定。这是一个基本的用户案例,描述了我想要发生的事情。我遇到的问题是当I :: ShellExecute()时,记事本说“进程无法访问该文件,因为它正被另一个进程使用”。 (这将是IHExplorer)。我需要解决这个问题,即使我仍然在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,以便其他进程(例如记事本)可以使用删除访问权限打开文件。

    请注意,我使用了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;
    }
    

    我认为如果我确定要传递给:: CreateFile的正确的SECURITY_DESCRIPTOR,它可能会像我想要的那样工作。请帮忙。

    顺便说一下,LaunchFile函数最终调用:: ShellExecute来启动文件。

1 个答案:

答案 0 :(得分:3)

重新阅读msdn doc后,我担心自己回答了自己的问题。 FILE_FLAG_DELETE_ON_CLOSE在关闭所有句柄后立即删除该文件,其中包括指定的句柄和任何其他打开或重复的句柄。如果文件存在打开的句柄,则调用将失败,除非它们都是使用FILE_SHARE_DELETE共享模式打开的。除非指定了FILE_SHARE_DELETE共享模式,否则对文件的后续打开请求将失败。在我的情况下,我怀疑记事本是否正在请求FILE_SHARE_DELETE权限,因此它无法打开文件