带有 FILE_FLAG_DELETE_ON_CLOSE 标志的 CreateFile
在描述我的问题之前,先介绍一下我正在编写的程序 (IHExplorer.exe):
这是一个 C++ 应用程序。
IHExplorer 应用程序看起来尽可能像 Windows 资源管理器窗口。但有一个例外,那就是从此资源管理器窗口中启动文件将首先将它们解密到用户的临时文件夹,然后启动与文件扩展名关联的应用程序并在关闭时删除该文件。
我遇到的问题是文件关闭时自动删除。这是一个场景:
- 用户双击 IHExplorer 中的加密 .txt 文件。
IHExplorer 解密内存中的 .txt 文件,然后使用 ::CreateFile 将其写入 %TEMP%,这会返回该文件的句柄(IHExplorer 必须至少保持此句柄打开,直到 .txt 文件被 shell 执行)。< /p>
IHExplorer Shell 从其临时位置执行 .txt 文件(通过调用 ::ShellExecute)。
- 现在 IHExplorer 和记事本都有打开文件的句柄。
- 当 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;
}
我认为,如果我确定要传递给 ::CreateFile 的正确 SECURITY_DESCRIPTOR,它可能会像我希望的那样工作。请帮忙。
顺便说一句,LaunchFile 函数最终只是调用 ::ShellExecute 来启动文件。
Before I describe my problem, here is a description of the program (IHExplorer.exe) I'm writting:
This is a C++ application.
The IHExplorer application is to look as much like a Windows Explorer window as possible. With one exception, and that is that launching files from within this Explorer window will decrypt them first to the user's temp folder, then launch the app associated with the file extension and delete the file on close.
The problem i'm having is with the auto delete when the file is closed. Here's a scenario:
- User double clicks an 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 has to keep this handle open atleast until the .txt file is shell executed).
IHExplorer Shell Executes the .txt file (by calling ::ShellExecute) from it's temp location.
- Now IHExplorer and notepad both have a handle to the file open.
- The file must be auto deleted when both IHExplorer and notepad have both closed their handle to the file, even if IHExplorer closes first.
ok. that is a basical User Case that describes what I want to happen. The problem I have is when I ::ShellExecute(), notepad says "The process cannot access the file because it is being used by another process." (which would be IHExplorer). I need to get around this and have notepad open it even while I still have the handle open in IHExplorer.
Here's what my call to ::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 I used FILE_SHARE_DELETE so that other processes (such as notepad) can open the file with delete access.
Notice that I used the FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE attributes to indicicate the the file is temporary and should be deleted on close.
Also notice the &sa parameter. This is the SECURITY_ATTRIBUTES structure that I am using, and I feel (hope) this is where my problem lies. Here is the code again, this time I will post the entire function so you can see how I fill out 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'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;
}
I think if I determine the correct SECURITY_DESCRIPTOR to pass to ::CreateFile it may work like I want it to. Please help.
btw, the LaunchFile function just ends up calling ::ShellExecute to launch the file.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
重新阅读 msdn 文档后,我担心我已经回答了我自己的问题。 FILE_FLAG_DELETE_ON_CLOSE 文件将在其所有句柄关闭后立即删除,其中包括指定的句柄和任何其他打开或重复的句柄。如果文件存在现有的打开句柄,则调用将失败,除非它们全部使用 FILE_SHARE_DELETE 共享模式打开。除非指定 FILE_SHARE_DELETE 共享模式,否则对该文件的后续打开请求将失败。就我而言,我怀疑记事本正在请求 FILE_SHARE_DELETE 权限,因此它无法打开该文件
After re-reading the msdn doc, I fear I have answered my own question. FILE_FLAG_DELETE_ON_CLOSE The file is to be deleted immediately after all of its handles are closed, which includes the specified handle and any other open or duplicated handles. If there are existing open handles to a file, the call fails unless they were all opened with the FILE_SHARE_DELETE share mode. Subsequent open requests for the file fail, unless the FILE_SHARE_DELETE share mode is specified. In my case, I doubt notepad is requesting the FILE_SHARE_DELETE permission, so it can't open the file