如何确定是否打开了一个文件以便在Windows上追加?

时间:2013-08-30 21:28:12

标签: winforms file winapi msvcrt

在UNIX中,如果我以附加模式打开文件,如

fd = open("filename", O_APPEND);

然后给出这样的文件描述符,可以使用fcntl轻松找出它打开的标志:

fcntl(fd, F_GETFL) & O_APPEND

我知道Windows上没有fcntl,但我想知道是否有某种方法可以确定这一点。 Windows确实支持附加模式,例如在使用CreateFile创建文件并传入FILE_APPEND_DATA标记时。

但是,如果我拥有的是已经打开的文件的句柄,我就无法找到一种方法来确定首次打开文件时请求的访问权限。 This question提供了检查特定文件访问权限的基本方法,但这似乎没有帮助。我试过了,即使我打开一个只读模式的文件,它仍然告诉我,我有FILE_APPEND_DATA 访问文件,如果我要求它。换句话说,此方法仅告诉我该进程对特定文件具有哪些访问权限(继承自启动该进程的用户)。它没有说明文件打开时请求的确切访问权限。

这与Windows如何跟踪文件是否只应附加到无关。这是后一个问题,我找不到任何答案。我发现最接近的是GetFileInformationByHandleEx但是在梳理完文档之后,没有一个文件属性可以通过该API返回,表示“附加模式”。

更新:为了更好地澄清我的问题,这个问题实际上只适用于MS VC运行时库 - 使用类似POSIX的函数_open打开的文件并写入fwrite等。看来本机win32文件句柄没有“附加模式”的概念。

4 个答案:

答案 0 :(得分:3)

对于 Windows 视为处于“追加”模式的文件,这将有效:

使用半文档化的NtQueryInformationFile系统调用(从ntdll.dll导出)来查询FILE_ACCESS_INFORMATION。这应该告诉你打开文件的访问掩码,它应该包含FILE_APPEND_DATA

对于 C运行时以“追加”模式打开的文件,但这不起作用,因为Windows实际上并未在追加模式下打开它。这样做的方法是以某种方式将文件描述符转换为文件对象,并检查那里的标志 - 但是没有记录的方法来执行此操作。

您可以查看Visual C ++ CRT源代码以了解如何执行此操作...可能有某种方法可以访问描述符所在的数组,但我不确定如何。
CRT中的代码似乎是_pioinfo(fd)->osfile |= FAPPEND,所以希望这会有所帮助。

答案 1 :(得分:3)

FileMode.Append是.NET Framework团队想象力的一个虚构。它不是Windows支持的模式。他们添加它以使原生winapi CreateFile()函数(FileStream)的包装更容易使用。 FileMode是其dwCreationDisposition参数的枚举包装器。哪个没有CREATE_APPEND选项。

FileStream.Init()方法中最相关的代码是:

   bool seekToEnd = (mode==FileMode.Append);
   // Must use a valid Win32 constant here...
   if (mode == FileMode.Append)
       mode = FileMode.OpenOrCreate;

换句话说,FileMode.Append与有效的CreateFile()选项不匹配,必须进行映射。如果存在则将打开该文件,否则将创建该文件。它负责额外的操作,打开文件后,它会寻找到文件的末尾。当您附加到现有文件时,您当然希望这样。存在一些额外的错误检查,它还确保您打开文件进行写入。

因此,在你的追求中发现了一个洞,以便发现这一点。 GetFileInformationByHandleEx()winapi函数可用于恢复文件状态。 Mehrdad未记录的原生api调用的记录功能。您可以从FileDispositionInfo返回dwCreationDisposition参数,以检查它是否为OpenOrCreate。但是,唯一,它也可以从使用客户端代码中的FileMode.OpenOrCreate打开的文件开始。很少见,但可能。

你失去的是寻找文件末尾的记忆。您可以使用FileStream.Position属性,但这当然会受到同时写入的任何影响。如果确实很重要,那么必须保留使用的FileMode值。

答案 2 :(得分:2)

自我回答,感谢@mehrdad和@HansPassant指出我正确的方向。实际上,MSVCRT导出一个名为ioinfo的结构数组,其中存储有关进程中每个打开文件句柄的信息。

结构的确切内容取决于VC版本和一些定义,但一般来说它的前两个成员是定义的:

typedef struct {
        intptr_t osfhnd;    /* underlying OS file HANDLE */
        char osfile;        /* attributes of file (e.g., open in text mode?) */
        ....
} ioinfo;

osfile成员很有意思 - 如果使用_O_APPEND打开文件,则会在此设置名为FAPPEND的{​​{1}}标记。

我在Python中基于CPython的posixmodule中的类似代码编写了一个小实用程序函数,可以执行此检查:https://gist.github.com/embray/6444262

答案 3 :(得分:2)

(我知道这是2013年以来的一个非常老的问题,但是我想与所有想直接从Windows File Handle获取文件模式 [读取,写入,APPEND] 的人分享我的解决方案)

NtQueryInformationFile API实际上可以为您提供足够的信息,以确定文件处于哪种模式。

让我们使用以下代码进行演示:

IO_STATUS_BLOCK statusBlock;
FILE_ACCESS_INFORMATION accessInfo;

// You have to import this API by yourself using LoadLibary and GetProcAddress
auto status = NtQueryInformationFile(
   yourFileHandle, &statusBlock, &accessInfo,
   sizeof(FILE_ACCESS_INFORMATION), (FILE_INFORMATION_CLASS)8);

auto flags = accessInfo.AccessFlags;

// true if in read mode, otherwise false
auto isRead = (FILE_READ_DATA & flags) != 0;
// true if in write mode, otherwise false
auto isWrite = (FILE_WRITE_DATA & flags) != 0;
// true if in write mode or append mode 
// (I think these 2 modes are mutual exclusive), otherwise false
auto isAppend = (FILE_APPEND_DATA & flags) != 0;

实际上非常简单:

  • 如果文件是使用GENERIC_READ(或FILE_READ_DATA)打开的,则设置FILE_READ_DATA标志,否则未设置;
  • 如果文件是使用GENERIC_WRITE(或FILE_WRITE_DATA)打开的,则设置FILE_WRITE_DATA标志,否则未设置;
  • 如果使用GENERIC_WRITE FILE_APPEND_DATA打开了文件,则将设置FILE_APPEND_DATA标志,否则未设置;

读取模式本身不会打开FILE_APPEND_DATA标志,附加模式本身不会打开FILE_WRITE_DATA标志。

我希望这可以帮助其他人。