最近在写C++程序的时候偶尔发现一个Windows上ofstream的小问题。Google搜了半天没搜出有用的结论,只是在一个不知名论坛的不知名帖子里偶尔提了一句,说可能是这个问题。根据那个人的帖子修改了之后果然work了。感慨于这么大的问题居然没人说,故而想写一篇博客记录一下。

场景大概是这样: 一个在"Program Files"文件夹下面的Binary文件,处于程序更新的目的,要被覆盖。更新的方式是先查询更新服务器得到新文件的Binary数组,暂存于内存中,然后再将原文件用ofstream打开,打开方式为ofstream::binary | ofstream::out。然后用ofstream.write()的方式将新文件的Binary数组写入文件中。

但是不work,或者更准确地说,是偶尔能work,但大部分时间不work,没有任何报错,但是当调用ofstream.close()之后,原文件没有发生变化。

网上搜了很多答案,有说是因为要加ofstream::trunc的,有说要用fstream的,有说要调用ofstream.flush的,都一一试过,都不行。

大概被这个问题搞了4个多小时,终于在一个几角旮旯里有人提到可能是该文件被别的进程占用了,或者某个进程对该文件留了个FILE HANDLE。这个倒是点醒了我,于是改用了Windows原汁原味的CreateFile来打开这个文件,create option用CREATE_ALWAYS。这样就可以重写并覆盖这个文件。(具体实例可见https://docs.microsoft.com/en-us/windows/desktop/fileio/opening-a-file-for-reading-or-writing)

仔细想想的确可能是某不知名进程对该文件仍留有了一个HANDLE,导致了C++ Level的ofstream在Windows上实现的时候底层的某些HANDLE检查导致其无法写入,所以写入时行时不行(有时有HANDLE conflict有时没有)。而用CreateFile打开该文件的时候则直接跳过了这些检查,直接写入。

以上也只是个人的一个猜想,毕竟没法看到Windows和Visual Studio对于ofstream的实现,所以无法确认。

但是如果下次有哪位同学遇到了同样的问题,能有幸搜索至此的话,希望这篇文章能为你节约4个小时的时间。结论就是:别纠结ofstream啦,直接上Windows Native API的CreateFile和WriteFile吧,稳定性高。

不知为何这个问题在网上都没个专家版的回答,难道现在用C++在Windows上写程序的人越来越少了?

小沐

阿沐公司创始人之一,曾在微软Redmond工作五年,喜欢钻研各种软件相关技术。最擅长的是系统设计和Windows编程,最不擅长的是编译器和人工智能。不过最近正在学习人工智能。