I have noticed strange behavior when continuosly write to file on Windows Server 2008 compared to Windows XP.
I execute from several threads something like that:
using (var fi = File.Open(...))
{
using (var fw = new StreamWriter(fi))
{
while program-is-running
{
fw.WriteLine(some-data);
fw.Flush();
}
}
}
When I observe file in Total Commander and Windows Explorer I see that file size keeps constant. I can walk into directory where file located, then walk out from directory, then walk into once again - file size is constant (in Total Commander as well as in Windows Explorer) until I manually refresh directory content in Windows Explorer by F5
When I run the same program in Windows XP - Total Commander and Windows Explorer continuosly show that file size is increasing.
I wonder what is the problem. Is it NTFS behaviour, software settings or something else?
Thank you in advance!
See this article on The Old New Thing. Basically, it has to do with efficiency. The up-to-date file metadata (such as its size) is not stored with the directory entry but with the file itself (this is similar to the way some UNIX filesystems work, and in contrast to the old FAT method).
However, the size is periodically transferred to the directory entry so that you can get a "last known" value. And, at some point in time (after the file has stopped changing), the last known value becomes accurate.
From that article:
In NTFS, file system metadata is a property not of the directory entry but rather of the file, with some of the metadata replicated into the directory entry as a tweak to improve directory enumeration performance. Functions like FindFirstFile report the directory entry, and by putting the metadata that FAT users were accustomed to getting "for free", they could avoid being slower than FAT for directory listings. The directory-enumeration functions report the last-updated metadata, which may not correspond to the actual metadata if the directory entry is stale.
The next question is where and how often this metadata replication is done; in other words, how stale is this data allowed to get? To avoid having to update a potentially unbounded number of directory entries each time a file's metadata changed, the NTFS folks decided that the replication would be performed only from the file into the directory entry that was used to open the file. This means that if a file has a thousand hard links, a change to the file size would be reflected in the directory entry that was used to open the file, but the other 999 directory entries would contain stale data.
Starting in Windows Vista (and its corresponding Windows Server version which I don't know but I'm sure you can look up, and by "you" I mean "Yuhong Bao"), the NTFS file system performs this courtesy replication when the last handle to a file object is closed. Earlier versions of NTFS replicated the data while the file was open whenever the cache was flushed, which meant that it happened every so often according to an unpredictable schedule. The result of this change is that the directory entry now gets updated less frequently, and therefore the last-updated file size is more out-of-date than it already was.
You can use WMI to get the correct filesize for an open file:
Using System.Management;
internal static ManagementObject GetFileManagementObject(string pfilepath)
{
string filepath = pfilepath.Replace("\\", "\\\\");
string scope = @"\\" + System.Environment.MachineName + @"\root\CIMV2";
string query = string.Format("Select FileSize from CIM_DataFile WHERE Name = '{0}'", filepath);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection collection = searcher.Get();
foreach (ManagementObject mobj in searcher.Get())
{
return mobj;
}
return (null);
}
private long GetFileSizeOpenFile(string path)
{
long fsize = -1;
try
{
ManagementObject mObj = Statics.GetFileManagementObject(path);
string sSize = mObj.Properties["FileSize"].Value.ToString();
long.TryParse(sSize, out fsize);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
return fsize;
}