文件的共享模式和访问权限

有用户反馈了这样一个问题:当Word或者Excel打开了一个文件时,无法用我们的程序打开该文件,而用其它程序却能够正常打开。经检查,这是由于在打开文件时CreateFile函数失败了,错误码是32,“另一个程序正在使用此文件,进程无法访问”。

我检查了一遍代码,“看上去”似乎一切正常。但是为什么其它程序可以正常打开,就只有我们的程序不行呢?为了对比两者在打开文件上的差异,我使用ProcessMonitor来观察分别用Notepad++和我们程序打开同一个文件的过程。

经过对比,可以发现两者在执行CreateFile操作时,共享模式(ShareMode)的值明显不一样,如下所示:

左图是Notepad++的CreateFile操作,ShareMode包含了Read和Write,操作结果为SUCCESS;右图是我们程序的CreateFile操作,ShareMode只有Read,操作结果为SHARING_VIOLATION。显然,问题出在调用CreateFile函数时传入的dwShareMode参数上,我们对该参数只传入了FILE_SHARE_READ。尝试加上FILE_SHARE_WRITE之后,问题解决了。

我又仔细阅读了一遍文档,发现一直以来我对共享模式的理解都不够全面(通过与其他人的交流,可以肯定不止我一个人是这样)。

共享模式实际上是用来与文件的访问权限进行校验的。假设进程A要打开一个文件,而该文件已经被进程B打开了,那么系统会进行以下校验:

  • 进程A传入的共享模式与进程B对该文件的访问权限(即打开文件时传入的dwDesiredAccess参数)是否一致;
  • 进程A传入的访问权限与进程B的共享模式是否一致。

共享模式与访问权限的一致性判断方式:

  • 如果共享模式包含读取,那么访问权限也必须包含读取;
  • 如果共享模式包含写入,那么访问权限也必须包含写入。

也就是说,系统会对双方的共享模式和访问权限进行交叉校验,只有两个校验都通过了,进程A才能打开文件,否则就会出现共享冲突错误。

所以,现在已不难理解为什么之前我们的程序打不开被Word和Excel打开的文件。Word和Excel必然对文件有写入权限,而我们的程序在打开文件时,共享模式只设置了读取,这意味着“我希望其它人只能读取该文件”,显然这是不可能的。

注意,为了便于理解,上面用了进程做例子,但实际上共享模式和访问权限都属于文件句柄的一部分,权限校验是针对句柄的。在一个进程中多次打开同一个文件,也会在文件句柄之间进行这些校验。