You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过编程强制Windows更新正在写入的文件的磁盘报告大小?

Windows下程序运行时日志文件大小在dir中显示异常的编程解决方法

问题描述

我开发了一个程序,会在事件触发时打开ASCII日志文件并向末尾写入文本行。事件触发时间不确定,可能每秒多次,也可能数小时一次。这部分功能正常,但遇到了Windows系统下的一个问题:程序运行时,在DOS shell里输入dir命令查看文件列表时,日志文件大小始终显示为0字节,直到程序关闭文件。有意思的是,如果直接输入dir my_file_name.log指定文件名,文件大小会正常显示,但仅输入dir就不行。

这是Windows的系统“特性”,临时解决方法是在DOS窗口执行type my_file_name.log > nul强制更新文件大小显示,但我想在程序内部通过编程实现同样的效果,不用用户手动操作。

我试过两种方法都没成功:

  • 模拟type命令的行为,单独调用fopen()和fclose()打开再关闭日志文件,没用;
  • 先调用fclose()关闭日志句柄,再用追加模式fopen()重新打开,但fopen()调用失败了(而且我也不想这么做,怕重新打开失败导致程序异常)。

示例程序:

#include <stdio.h>
#include <windows.h>

int main(int args, char ** argv)
{
   FILE * fpOut = fopen("my_log_file.txt", "w");
   if (fpOut)
   {
      for (int i=0; i<1000; i++)
      {
         fprintf(fpOut, "Event %i occurred\n", i);
         fflush(fpOut);

         Sleep(1000);

         // 这里能调用什么让文件大小对外可见?
      }
      fclose(fpOut);
   }
   return 0;
}

解决方案

方法1:基于C标准库+Windows API刷新元数据

核心思路是通过Windows API强制刷新文件的元数据和目录缓存,让dir命令能立即获取到正确的文件大小。修改后的代码如下:

#include <stdio.h>
#include <windows.h>
#include <io.h> // 用于_get_osfhandle

int main(int args, char ** argv)
{
   FILE * fpOut = fopen("my_log_file.txt", "w");
   if (fpOut)
   {
      // 从FILE*获取底层Windows文件句柄
      HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(fpOut));
      if (hFile == INVALID_HANDLE_VALUE)
      {
         fclose(fpOut);
         return 1;
      }

      for (int i=0; i<1000; i++)
      {
         fprintf(fpOut, "Event %i occurred\n", i);
         fflush(fpOut);

         // 刷新文件元数据并更新目录缓存
         LARGE_INTEGER currentSize;
         if (GetFileSizeEx(hFile, &currentSize))
         {
             // 重置文件指针到末尾,触发元数据同步
             SetFilePointerEx(hFile, currentSize, NULL, FILE_BEGIN);
             SetEndOfFile(hFile);
             // 强制将所有数据和元数据写入磁盘
             FlushFileBuffers(hFile);

             // 通过查询文件信息更新目录缓存
             WIN32_FIND_DATA fileData;
             HANDLE hFind = FindFirstFile("my_log_file.txt", &fileData);
             if (hFind != INVALID_HANDLE_VALUE)
             {
                 FindClose(hFind);
             }
         }

         Sleep(1000);
      }
      fclose(fpOut);
   }
   return 0;
}

方法2:直接使用Windows API操作文件

如果可以替换掉C标准库的文件操作,直接用Windows原生API能更精准控制缓存行为,代码如下:

#include <windows.h>

int main(int args, char ** argv)
{
   // 创建可共享读写的文件句柄,允许其他进程读取
   HANDLE hFile = CreateFile("my_log_file.txt", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
                             NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
   if (hFile != INVALID_HANDLE_VALUE)
   {
      for (int i=0; i<1000; i++)
      {
         char buffer[64];
         sprintf(buffer, "Event %i occurred\n", i);
         DWORD bytesWritten;
         WriteFile(hFile, buffer, strlen(buffer), &bytesWritten, NULL);

         // 刷新数据和元数据到磁盘
         FlushFileBuffers(hFile);

         // 更新目录缓存
         WIN32_FIND_DATA fileData;
         HANDLE hFind = FindFirstFile("my_log_file.txt", &fileData);
         if (hFind != INVALID_HANDLE_VALUE)
         {
             FindClose(hFind);
         }

         Sleep(1000);
      }
      CloseHandle(hFile);
   }
   return 0;
}

原理说明

  • FlushFileBuffers():强制把文件的缓存数据和元数据写入磁盘,但仅这一步还不够,因为系统的目录缓存可能没同步;
  • FindFirstFile():主动读取文件的目录信息,触发系统更新目录缓存,这样后续的dir命令就能拿到最新的文件大小;
  • 方法1中的SetEndOfFile():确保文件大小的元数据被正确同步,哪怕写入的数据刚好填满缓存块,也能触发更新。

内容的提问来源于stack exchange,提问作者Jeremy Friesner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 09:20:44