【问题标题】:How to check if a file is already open by another process in C?如何检查文件是否已被C中的另一个进程打开?
【发布时间】:2010-12-29 10:29:02
【问题描述】:

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

【问题讨论】:

  • 为什么要查明文件是否被另一个进程打开?另外,您想知道文件是由特定(即已知)进程打开还是仅由任何其他进程打开?
  • 如果另一个进程已经同时访问。如果该文件正在使用中,我可能会在继续操作时损坏某些东西。
  • 我对“哪个”进程正在访问信息不感兴趣。这个问题在这里是因为似乎没有其他人在其他平台上回答这个问题,但可能有必要知道这一点。所以请保持简短,不要告诉“重新思考”这个问题。想想资源管理器,就好像在删除文件之前它会检查是否有其他进程正在访问它。
  • 根据您的回答,您遇到了文件锁定问题,每个平台都有明确的方法来解决这个问题。至于对重新思考问题不感兴趣,我只会注意到将其从“谁打开文件”更改为“我如何锁定文件”正在重新思考问题,并将其从一个非常把难的变成更简单的。
  • "每个平台"?!?!?你知道有多少人吗?

标签: c windows file


【解决方案1】:

任何这样的检查本质上都是不道德的。另一个进程总是可以在您进行检查的点和您访问文件的点之间打开文件。

【讨论】:

    【解决方案2】:

    获取 open_files 信息很困难,就像拔牙一样,如果您没有立即需要它,您不应该仅仅为了它而要求“每个平台的几个示例”。当然,这只是我的看法。

    Linux 和许多 Unix 系统都有一个名为 lsof 的系统实用程序,它可以查找打开的文件句柄和内容。它这样做的方式是访问/dev/kmem,这是一个包含“实时”内核内存副本的伪文件,即操作系统内核的工作存储。里面自然有打开文件的表格,而且内存结构是开源的和文档化的,所以 lsof 去那里找到信息并为用户格式化只是一件很忙的事情。

    另一方面,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,以便与您的程序一起加载。

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

    【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 谢谢。对于 Windows,这很有效,并且确实帮助了我的问题。剩下的就是给其他寻求其他平台答案的人了。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      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
         CloseHandle(fh);
      }
      

      MS 说:dwShareMode

      对象的共享方式,可以是read、write、both、delete、all这些,也可以none(参考下表)。

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

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

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

      扩展名: 如何删除没有人打开读/写的(非只读)文件系统?

      访问权限 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()
                                */
          CloseHandle(fh); /* a file, which no one has currently opened for RW is delete NOW */
      
      } 
      

      如何处理打开的文件?如果文件已打开并且您被允许取消链接,您将留下一个文件,后续打开将导致 ACCESS_DENIED。 如果您有一个临时文件夹,那么最好重命名(filename, tempdir/filename.delete) 并删除 tempdir/filename.delete。

      【讨论】:

        【解决方案6】:

        这不可能是那么难的家伙。

        这样做:

        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){
        
        }
        

        【讨论】:

        • 真的,哈哈,抱歉
        • 您使用的是哪个“版本”的 C++?
        【解决方案7】:

        对于 Windows,此代码也适用:

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

        打开的文件不能重命名,重命名为同名不会导致另一个错误。因此,如果重命名成功,但没有真正做任何事情,你就知道文件没有打开。

        【讨论】:

        • 这对我有用,而且实现起来非常简单!它使用 Java 而不是 C,这也许就是您被否决的原因。我发现这个答案非常有帮助,谢谢。
        • 您使用哪个库进行重命名操作?因为如果我尝试使用std::rename 重命名文件,Windows 允许重命名文件,尽管它已打开。
        【解决方案8】:

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

         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;
            }
        

        【讨论】:

          猜你喜欢
          • 2010-11-06
          • 2012-03-09
          • 1970-01-01
          • 2014-11-22
          • 2010-09-29
          • 1970-01-01
          • 1970-01-01
          • 2016-09-27
          • 1970-01-01
          相关资源
          最近更新 更多