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

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

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

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

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

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

  4. 现在 IHExplorer 和记事本都有打开文件的句柄。
  5. 当 IHExplorer 和记事本都关闭文件句柄时,即使 IHExplorer 首先关闭,也必须自动删除该文件。

好的。这是一个基本的用户案例,描述了我想要发生的事情。我遇到的问题是当我::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;
}

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

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

有帮助吗?

解决方案

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top