是否有标准文件保存和交换模式?

时间:2018-06-11 18:52:17

标签: c++ windows file file-handling

我们的产品在每个打开的文档文件上都有一个写独占文件句柄,以确保我们对该文件具有独占的写入控制权。

因此,Windows不允许任何其他进程执行更多操作,而不是从文件中读取文件,也不能从资源管理器或其他进程中删除文件 - 因为它上面有打开(并且写独占)句柄。

我们一直遇到麻烦,但是文件的内容已经损坏。我认为它与错误有关,或者可能是我对Windows API保证的误解 - 即为了将设计文件保存在以前版本的顶部 - 我们目前持有文件句柄 - 我必须回放句柄开始文件,写出来,然后强制它冲洗&截断该新位置(如果文件缩小 - 我们不希望在文件末尾有额外的污泥 - 这也是一种腐败形式)。在会话期间多次执行此操作 - 每次用户编辑然后保存其更改...

但是,有时我们的客户会报告所有这些损坏的文件(仅通过网络 - 从不在本地)。

我们认为这可能是因为我们的实际保存过程稍微复杂一些:

  1. 回放(已打开的)文件句柄
  2. 写出核心数据
  3. flush&截断句柄
  4. fseek到文件末尾
  5. 写出缩略图图像数据(基本上是 - 附上缩略图)
  6. flush&截断句柄
  7. 这可能仅仅是“不要冲洗,寻找,写入,冲洗”的情况。 - 这会在MS的网络文件系统代码中引入微妙的错误(或者依赖于系统中内置的不确定性 - 并且无法可靠地依赖)?

    所以,我实施了两层修复:

    1. 做一个倒带,然后写核心数据+图像数据+刷新&截断(一次)
    2. 执行“保存为临时”,“关闭”,“重命名”
    3. 没有。 2有一些不错的功能 - 例如"如果写出新文件时出现问题,旧文件保持不变。"这意味着在最坏的情况下,他们的新数据不会被保存,但不会丢失旧数据。

      它是经典模式的基本用法"构建新副本然后将其交换到真实/主动数据结构。"

      很棒 - 但我不知道的是如何交换文件的内容"?

      我可以做经典:

      1. 完全写入T(temp)并关闭它。
      2. 将A(实际)文件重命名为A.bak。
      3. 将T重命名为A
      4. (当然我首先需要删除任何先前的A.bak)。

        这很好 - 但是再次 - 我们通常在A上有一个锁定的句柄。所以这扩展到有点不完美:

        1. 写T
        2. 关闭我们在A
        3. 上的句柄
        4. 将A重命名为A.bak
        5. 将T重命名为A
        6. 在A
        7. 上获取写独占句柄

          我对此不喜欢的是太多移动部件。"

          • 在2到5之间,任何人都可以锁定A或以其他方式妨碍我们。

          你认为它不会发生 - 但随后文件系统索引或防病毒软件或备份软件都会受到影响而且非常非常经常(根据我们的经验)。

          所以 - 理想情况下,我不想在任何时候放弃对A的控制!我希望确保每次切换都不会受到防病毒软件或其他软件的影响而无法进入并剔除。

          理想情况下,事实上,我是:

          1. 写T
          2. 交换T和A的内容(要求文件系统将名称A实际链接到T的内容)
          3. 永远幸福地生活......
          4. 那么,是否有其他人发现用于交换T和A的模式?

            是否有一组API调用可以使其更好/更强大?

            完全可能有助于重新思考我的方法的其他想法?

            注意:MS已弃用事务性文件系统API。所以这听起来像一个非首发 - 更不用说它在Windows下的所有文件系统上都不可用。

            更新: FWIW,我将其实现为写入临时文件,重命名原始文件,将temp重命名为real,删除原始文件(加上必要的解锁并获取新锁定),使用RAII和ScopeGuard处理任何故障回滚,虽然当然回滚 - 是副作用和依赖于操作系统,是最好的案例场景"而且不像C ++语言本身那样保证。尽管如此,在测试过程中它非常有效 - 永远不会给我一个糟糕的文件(我有意无意地创建了一些问题,这些问题在调用unroll进程的算法中产生了一个错误的临时文件或者错误(引发了异常))。 / em>的

            更新2: " Final"算法是为了     1.(保存到临时的本地验证副本)
                2.保存到临时新文件
                3.(验证新保存和验证匹配)
                4.放下我们对真实档案的锁定     5.将实际文件重命名为临时旧文件,并将原始文件替换为临时文件(包括传输属性,ACL和时间戳 - 请参阅ReplaceFile())
                6.获取我们的锁(如果它被锁定)
                7.成功(丢弃我们的警卫)

1 个答案:

答案 0 :(得分:1)

ReplaceFile是相当标准的方法。

它不能解决锁定问题,但是它是您实施的测试版,具有其他功能,例如将任何ACL从旧文件传输到新文件。

如果您保留自制软件的实现,请确保处理Windows上的文件删除并非总是即时的情况。例如,如果在将当前文件重命名为备份文件之前必须删除旧的“备份”文件,则重命名可能会失败。