前言
最近开发相关功能的时候,涉及到了文件资源被占用的问题。这里做一下记录
情景再现
我有一个文件资源的对象FileRes,它有唯一对应的一个文件File,此时我有一个图片的变量var,使用了File文件,于是乎,var手握File的文件句柄。
现在有个操作,需要删除FileRes对象,并且将对应的文件也删掉,此时var占据着File的文件句柄,删除肯定是失败的,所以要先判断这个文件是否被占有,如果占有了,我们要提示删除失败,如果没有被占有,才删除。
解决方案
因为涉及到Windows的API,不是很熟,所以就是网上找了一大堆,最后可算是找到了一个,这里列举了两个方案,但是在我的应用场景里面,只有方案一可行
,具体原因见下文
方案一:
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/locking.h>
#include <share.h>
#include <fcntl.h>
bool IsFileUsed(const char* filePath)
{
bool ret = false;
int fh = _sopen(filePath, _O_RDWR, _SH_DENYRW, _S_IREAD | _S_IWRITE );
if(-1 == fh)
ret = true;
else
_close(fh);
return ret;
}
这段代码的功能是判断指定的文件是否正在被使用(被其他进程锁定或打开)。
包含的头文件,这些都是 Windows 平台下的系统头文件,提供了文件操作相关的函数和常量定义。
核心函数是IsFileUsed,它的工作原理如下:
函数尝试以_sopen函数打开指定路径的文件
参数_O_RDWR
表示以读写方式打开
参数_SH_DENYRW
是关键,它表示拒绝其他任何进程对该文件的读写访问
参数_S_IREAD | _S_IWRITE
设置文件的读写权限
判断文件是否被使用的逻辑:
如果_sopen返回 - 1,表示文件打开失败
,失败的可能原因是文件正在被其他进程使用(已被锁定或打开)
这时函数返回true,表示文件正在被使用
如果成功打开文件,则立即用_close
关闭它,并返回false,表示文件未被使用
这段代码利用了 Windows 文件系统的锁定机制:如果一个文件已经被其他进程打开且没有共享权限,那么尝试以独占方式打开它就会失败。通过这种方式间接判断文件是否正在被使用。
方案二:
bool IsFileLocked(const char* filePath)
{
HANDLE hFile = CreateFileA(filePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// 文件打开失败,说明文件被独占
return true;
}
else
{
// 文件打开成功,关闭文件句柄
CloseHandle(hFile);
return false;
}
}
这段代码定义了一个名为IsFileLocked
的函数,用于判断指定的文件是否被其他进程锁定(处于无法共享访问的状态)。它使用了Windows API来实现这一功能,具体解释如下:
函数的工作原理基于Windows文件系统的共享访问机制:
-
使用
CreateFileA
函数尝试打开目标文件- 第一个参数
filePath
:要检查的文件路径 - 第二个参数
GENERIC_READ
:以只读方式打开文件 - 第三个参数是关键:
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
表示允许其他进程同时对该文件进行读、写和删除操作 - 第四个参数
NULL
:使用默认的安全属性 - 第五个参数
OPEN_EXISTING
:只打开已存在的文件 - 第六个参数
FILE_ATTRIBUTE_NORMAL
:使用常规文件属性 - 最后一个参数
NULL
:没有模板文件
- 第一个参数
-
判断逻辑:
- 如果
CreateFileA
返回INVALID_HANDLE_VALUE
(无效句柄),说明文件打开失败 - 失败的主要原因通常是文件被其他进程以独占方式打开(不允许共享),此时函数返回
true
表示文件被锁定 - 如果成功打开文件,函数会立即用
CloseHandle
关闭文件句柄,并返回false
表示文件未被锁定
- 如果
需要注意的是,这个函数判断的是"文件是否被独占锁定",而不是物理意义上的锁定。它通过尝试以最大共享权限打开文件来推断文件是否被其他进程独占使用。
与前一个使用_sopen
的实现相比,这个版本直接使用了Windows API,功能上更为接近系统底层,同样也是Windows平台特有的实现。
方案比较
这两段代码虽然都是用于判断文件是否被占用,但在实现方式、判断逻辑和细节行为上有明显区别,主要差异如下:
1. 底层实现方式不同
-
第一段代码(
IsFileUsed
):
使用C运行时库函数_sopen
(微软扩展函数),属于标准C库的扩展,封装了底层系统调用。 -
第二段代码(
IsFileLocked
):
直接调用Windows系统APICreateFileA
,属于更底层的系统接口,是Windows平台特有的文件操作函数。
2. 判断逻辑的核心差异
-
第一段代码:
尝试以_SH_DENYRW
(拒绝其他进程读写) 的独占模式打开文件。- 如果打开失败(返回-1),说明文件已被其他进程占用(因为无法获取独占权限),返回
true
。 - 本质是:“我能否独占这个文件?不能则表示文件被使用”。
- 如果打开失败(返回-1),说明文件已被其他进程占用(因为无法获取独占权限),返回
-
第二段代码:
尝试以FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
(允许所有共享) 的最大共享模式打开文件。- 如果打开失败(返回
INVALID_HANDLE_VALUE
),说明文件被其他进程以独占方式锁定(不允许任何共享),返回true
。 - 本质是:“我能否以最宽松的共享方式打开文件?不能则表示文件被独占锁定”。
- 如果打开失败(返回
3. 对“文件被占用”的判断范围不同
-
第一段代码的判断更宽泛:
只要文件无法以独占方式打开(包括被其他进程以非独占方式打开但占用了部分权限),就会判定为“被使用”。例如,其他进程以“允许读但禁止写”的方式打开文件时,_sopen
会失败。 -
第二段代码的判断更严格:
只有当文件被其他进程以完全独占(不允许任何共享)的方式打开时,才会判定为“被锁定”。如果其他进程以“允许共享读”的方式打开文件,CreateFileA
仍能成功打开,返回false
。
4. 适用场景不同
-
IsFileUsed
:适合判断文件是否“正在被任何进程访问”(包括非独占访问),例如检查文件是否被编辑中。 -
IsFileLocked
:适合判断文件是否“被严格锁定”(如被进程独占写入),例如检查文件是否被锁定以防止任何修改。
两段代码的核心区别在于打开文件时请求的共享权限相反:
IsFileUsed
尝试独占文件,失败则认为文件被使用;IsFileLocked
尝试最大程度共享文件,失败则认为文件被严格锁定。
选择哪段代码取决于需要判断的“文件被占用”的严格程度。
总结
方案二我自己尝试为什么不行,其实就在于文件资源File只是被占用,并且是以非独占的方式访问,所以就导致第二种方式获取到的判定结果和我预想的不一致
澄澈i
用简单的语言记录自己走过的技术路