Before describing my problem, here is a description of the program (IHExplorer.exe) that I am writing:
This is a C ++ application.
The IHExplorer application should look like a Windows Explorer window, as far as possible. With one exception, and this is that launching files from this Explorer window first decrypts them into the user's temporary files folder, then run the application associated with the file extension and delete the file when closing.
The problem I am facing is the automatic deletion when the file is closed. Here is the scenario:
- The user double-clicks the encrypted .txt file in IHExplorer.
IHExplorer decrypts the .txt file in memory, then writes it to% TEMP% using :: CreateFile, which returns a HANDLE to the file (IHExplorer should keep this handle open at least until the .txt file is executed by the shell).
IHExplorer Shell Executes a .txt file (by calling :: ShellExecute) from its temporary location.
- Now IHExplorer and Notepad have an open file descriptor.
- The file should be automatically deleted when both IHExplorer and Notepad closed their file descriptor, even if IHExplorer closes first.
OK. This is a basic example of a user that describes what I want. The problem I have is when I :: ShellExecute (), the notepad says: "The process cannot access the file because it is being used by another process." (which will be IHExplorer). I need to get around this and open the notebook, even when I still open the pen in IHExplorer.
This is what my call :: CreateFile looks like:
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);
Notice that I used FILE_SHARE_DELETE so that other processes (such as notepad) could open the file with remote access.
Note that I used FILE_ATTRIBUTE_TEMPORARY | Attributes FILE_FLAG_DELETE_ON_CLOSE for the designation of the file are temporary and must be removed when closing.
Also note the & sa option. This is the SECURITY_ATTRIBUTES structure that I am using and I feel (hopefully) that this is my problem. Here is the code again, this time I will post the whole function so you can see how I fill the SECURITY_ATTRIBUTES structure:
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 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 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; }
I think that if I define the correct SECURITY_DESCRIPTOR to go to :: CreateFile, it can work the way I want. Please, help.
btw, the LaunchFile function ends with a call to :: ShellExecute to start the file.