11

我正在尝试从缓冲区文件中读取二进制数据,该文件由不同的进程(我无法修改)连续写入。我正在使用以下代码来打开文件:

fileH = CreateFileA((LPCSTR)filename,
                    GENERIC_READ,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL, NULL);

它正确打开,没有错误。但是,当我从文件中读取数据时,它似乎阻止了其他进程写入文件,因为我丢失了数据。

缓冲区是循环的,这意味着文件大小是固定的,新数据会不断地覆盖缓冲区中的旧数据。

编辑: 有时最简单的解决方案有效......

我已经联系了软件公司并告诉了他们这个错误,并且在一天之内他们发布了一个带有修复的新版本。抱歉,这不能适用于所有人。

4

2 回答 2

8

如果不知道写入过程如何打开文件,很难说出您的选择是什么。显然,它不是以独占方式打开文件并保持打开状态。否则你根本无法阅读它。

您描述的行为表明写入过程以独占方式打开文件,写入文件,然后关闭文件。如果是这种情况,那么您不能让您的程序打开文件并保持打开状态。这将导致写入过程在尝试写入时失败。

如果你不能修改写作过程,那么你的选择是有限的,不是很有吸引力。最有可能的是,您必须让程序打开文件,读取一小块,关闭文件,然后在再次读取之前稍等片刻。即使这样,也不能保证在写入过程尝试写入时不会打开文件。我想,你已经发现了。

您知道写入过程是在无法打开文件时丢失数据,还是只是缓冲数据并在下次实际打开文件时写入?如果是这种情况,那么我建议一次逐步浏览文件的建议可能会奏效。否则,您将丢失数据。

据我所知,没有一种打开模式相当于“打开文件进行阅读,但如果有人想要独占访问,那就让他们拥有它”。

另一种可能性是让您的程序在您想阅读时重命名文件,然后在阅读后删除重命名的文件。当然,这假设写入过程将在必要时创建一个新文件。即使那样,如果写入过程在您重命名时尝试写入,也可能会出现问题。我不认为这会是一个问题(就文件系统而言,重命名可能是原子的),但这是你必须研究的东西。

于 2013-03-13T14:59:32.630 回答
5

我建议查看出色的Far Manager的源代码。它的内部查看器可以轻松处理数千兆字节的文件,显示正在写入的文件没有问题,并且几乎可以实时更新更改的文件内容。我从未注意到正在显示的文件有任何阻塞问题。

与问题相关的源代码似乎在viewer.cpp文件中。

一件有趣的事情是它使用GENERIC_READ

ViewFile.Open(strFileName, FILE_READ_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, nullptr, OPEN_EXISTING);

我怀疑下降SYNCHRONIZE在这里可能很重要。

文件更改检测在Viewer::ProcessKey,KEY_IDLE情况下:

// Smart file change check -- thanks Dzirt2005
//
bool changed = (
    ViewFindData.ftLastWriteTime.dwLowDateTime!=NewViewFindData.ftLastWriteTime.dwLowDateTime ||
    ViewFindData.ftLastWriteTime.dwHighDateTime!=NewViewFindData.ftLastWriteTime.dwHighDateTime ||
    ViewFindData.nFileSize != NewViewFindData.nFileSize
);
if ( changed )
    ViewFindData = NewViewFindData;
else {
    if ( !ViewFile.GetSize(NewViewFindData.nFileSize) || FileSize == static_cast<__int64>(NewViewFindData.nFileSize) )
        return TRUE;
    changed = FileSize > static_cast<__int64>(NewViewFindData.nFileSize); // true if file shrank
}

缓存文件读取在cache.cpp中实现。但是那里并没有什么真正惊天动地的东西,只有一些Seek()Read()最终导致API调用)。不使用重叠。SetFilePointerExReadFile

于 2013-03-13T14:10:33.510 回答