如何检查文件是否已被C中的其他进程打开?

时间:2009-12-23 09:57:46

标签: c windows file

我看到标准C无法判断文件是否已在另一个进程中打开。所以答案应该包含每个平台的几个例子。我需要检查Visual C ++ / Windows。

8 个答案:

答案 0 :(得分:8)

windows:尝试以独占模式打开文件。如果它工作,没有其他人打开该文件,将无法打开文件

HANDLE fh;
fh = CreateFile(filename, GENERIC_READ, 0 /* no sharing! exclusive */, NULL, OPEN_EXISTING, 0, NULL);
if ((fh != NULL) && (fh != INVALID_HANDLE_VALUE))
{
   // the only open file to filename should be fh.
   // do something
   CloseHande(fh);
}

MS说:dwShareMode

  

对象的共享模式,可以是read,write,both,delete,all,all或none(参见下表)。

     

如果此参数为零且CreateFile成功,则无法共享该对象,并且在关闭句柄之前无法再次打开该对象。

     

您不能请求与具有打开句柄的打开请求中指定的访问模式冲突的共享模式,因为这会导致以下共享冲突:ERROR_SHARING_VIOLATION。

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx

延伸: 如何删除没有人打开读/写的(非readonly)文件文件系统?

访问权限FILE_READ_ATTRIBUTES,而不是DELETE。 DELETE可能会导致smb共享问题(对于MS Windows服务器) - CreateFile将以一个仍然打开的FileHandle / Device / Mup:xxx文件名离开 - 为什么以及这个Mup是什么。访问权限FILE_READ_ATTRIBUTES不会发生 使用FILE_FLAG_OPEN_REPARSE_POINT删除文件名。否则,您将删除符号链接的目标 - 这通常不是您想要的

HANDLE fh;
fh = CreateFile(filename,  FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE /* no RW sharing! */, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_DELETE_ON_CLOSE, NULL);
if ((fh != NULL) && (fh != INVALID_HANDLE_VALUE))
{
    DeleteFile(filename); /* looks stupid?
                          * but FILE_FLAG_DELETE_ON_CLOSE will not work on some smb shares (e.g. samba)!
                          * FILE_SHARE_DELETE should allow this DeleteFile() and so the problem could be solved by additional DeleteFile()
                          */
    CloseHande(fh); /* a file, which no one has currently opened for RW is delete NOW */

} 

如何处理打开的文件?如果文件已打开且您可以进行取消链接,则会留下一个文件,后续打开将导致ACCESS_DENIED。 如果你有一个temoprary文件夹,那么重命名(filename,tempdir / filename.delete)和删除tempdir / filename.delete是一个好主意。

答案 1 :(得分:7)

除非另一个进程明确禁止访问该文件,否则无法判断。在MSVC中,您可以使用_fsopen()执行此操作,为shflag参数指定_SH_DENYRD。对于是否打开未被锁定的文件感兴趣的概念在多任务操作系统上存在严重缺陷。它可能会在您发现它不是之后打开一微秒。这也是Windows没有IsFileLocked()函数的原因。

如果您需要对文件进行同步访问,则需要使用命名的互斥锁添加它,使用CreateMutex()。

答案 2 :(得分:4)

对于Windows,此代码也有效:

boolean isClosed(File f) { return f.renameTo(f); }

无法重命名已打开的文件,并且重命名为同名不会导致其他错误。因此,如果重命名成功,没有真正做某事,你就知道文件没有打开。

答案 3 :(得分:3)

获取open_files信息非常困难,就像拔牙一样,如果你没有立即需要它,你就不应该为它寻求“每个平台的几个例子”。当然,只是我的意见。

Linux和许多Unix系统都有一个名为lsof的系统实用程序,可以找到打开的文件句柄和东西。它这样做的方法是访问/dev/kmem,它是一个伪文件,包含“实时”内核内存的副本,即操作系统内核的工作存储。那里有开放文件表,当然,内存结构是开源的并且有文档记录,所以只需要很多繁忙的工作就可以让lsof进去,找到信息并为用户格式化。 / p>

另一方面,Windows的深层内部文档实际上是不存在的,我不知道数据结构是以某种方式暴露给外部的。我不是Windows专家,但除非Windows API明确提供此类信息,否则可能无法使用。

Mark Russinovich的SysInternals公用事业可能正在使用任何可用的东西;想到的第一个是FileMon。看那些可能会给你一些线索。 更新:我刚刚获悉SysInternals Handles.exe 更接近您想要的内容。

如果你设法弄明白,那就好;否则你可能会对文件打开/关闭操作感兴趣:Windows API提供了大量所谓的Hooks:http://msdn.microsoft.com/en-us/library/ms997537.aspx。钩子允许您在系统中发生某些事情时请求通知。我相信有一个会告诉你程序 - 系统范围 - 打开文件的时间。因此,您可以在听到挂钩的过程中打开自己的文件列表。我不确定,但我怀疑这可能是FileMon所做的。

可以从C访问Windows API,包括钩子函数。系统范围的钩子将要求您创建一个DLL,以便与您的程序一起加载。

希望这些提示可以帮助您入门。

答案 4 :(得分:1)

任何此类检查本质上都是有趣的。另一个进程总是可以在您执行检查的位置和访问该文件的位置之间打开文件。

答案 5 :(得分:0)

到目前为止的答案应该告诉您,找到您要求的信息是棘手的,不可移植的,并且通常本质上不可靠。所以,从我的角度来看,真正的答案是不要那样做。试着找到一种方法来思考你的真实问题,这样就不会出现这个问题。

答案 6 :(得分:0)

这不是那么难的人。

这样做:

try{
File fileout = new File(path + ".xls");
FileOutPutStream out = new FileOutPutStream(fileout);
}
catch(FileNotFoundException e1){

 // if a MS Windows process is already using the file, this exception will be thrown
}
catch(Exception e){

}

答案 7 :(得分:0)

您可以使用类似这样的东西。这不是一个适当的解决方案。但这有效,

 bool IsFileDownloadComplete(const std::wstring& dir, const std::wstring& fileName) 
    {
        std::wstring originalFileName = dir + fileName;
        std::wstring tempFileName = dir + L"temp";

        while(true)
        {
            int ret = rename(convertWstringToString(originalFileName).c_str(), convertWstringToString(tempFileName).c_str());
            if(ret == 0)
                break;      

            Sleep(10);
        }   

        /** File is not open. Rename to original. */
        int ret = rename(convertWstringToString(tempFileName).c_str(), convertWstringToString(originalFileName).c_str());
        if(ret != 0)
            throw std::exception("File rename failed"); 

        return true;
    }