在Windows上编程实现重命名打开的文件

18

我正在将一个Unix C应用程序移植到Windows。该应用程序在文件打开时重命名文件,在Unix上完全没有问题,但显然在Windows上无法正常工作。追踪所有的重命名操作,确保我关闭文件,然后重新打开和定位会很痛苦。

考虑到Windows资源管理器允许在使用中重命名文件,我想知道为什么我无法使这个功能正常工作。我已尝试使用C中的renameMoveFile,以及C#中的System.IO.File.Move。在所有情况下都会失败,并显示“权限被拒绝”的错误(具体而言,由GetLastError()返回的错误是“The process cannot access the file because it is being used by another process”)

任何提示?

我还尝试使用_sopen以共享模式打开文件,但也没有成功(同样的错误)。

感谢Stefan提供的工作中的C#代码:

string orig_filename = "testrenamesharp-123456";
string dest_filename = "fancynewname.txt";
Byte[] info = new UTF8Encoding(true).GetBytes("This is to test the OpenWrite method.");
var fs = new FileStream(orig_filename, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete);
fs.Write(info, 0, info.Length);
File.Move(orig_filename, dest_filename);
fs.Close();

以下是一份可用的C语言示例:

const char* filename = "testrename-XXXXXX";
const char* dest_filename = "fancynewname.txt";

/* The normal POSIX C functions lock the file */
/* int fd = open(filename, O_RDWR | O_CREAT, _S_IREAD | _S_IWRITE); */ /* Fails */
/* int fd = _sopen(filename, O_RDWR | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); */ /* Also fails */

/* We need to use WINAPI + _open_osfhandle to be able to use 
   file descriptors (instead of WINAPI handles) */
HANDLE hFile = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL );
if( INVALID_HANDLE_VALUE == hFile) {
    ErrorExit(TEXT("CreateFile"));
}

int fd = _open_osfhandle((long int)hFile, _O_CREAT | _O_RDWR | _O_TEMPORARY);
if( -1 == fd ) {
    perror("open");
}

int resw = write(fd, buf, strlen(buf));
if(-1 == resw) {
    perror("write");
}

if( 0 == access(dest_filename, F_OK)) {
    perror("access");
}

/* Now try to rename it - On Windows, this fails */
int resr = rename(filename, dest_filename);
if( -1 == resr) {
    perror("rename");
}

int resc = close(fd);
if( -1 == resc ) {
    perror("close");
}

你遇到了哪种类型的故障? - StevenV
2
Windows资源管理器允许在文件被锁定且正在使用时进行重命名?从什么时候开始允许的? - Doc Brown
@Doc Brown:我并没有告诉open/_sopen锁定文件(没有_O_EXCL标志)。事实上,相反的是:使用_sopen时,我明确要求共享该文件。 - zxspectrum
如果 Stefan 的帖子回答了你的问题,你应该将其标记为答案 :) 如果没有回答你的问题,你可能需要解释为什么它没有回答。 - Tobbe
我刚遇到了这个问题(由于IIS必须重命名锁定文件),并使用了Stefan的解决方案。然而,当创建FileStream时,我一直收到权限被拒绝的错误。我通过使用FileAccess.Read而不是FileAccess.ReadWrite来纠正了这个问题。 - Daryl
2个回答

22
重命名需要使用FileShare.Delete共享方式打开要操作的文件。如果此共享标志缺失,则在该文件仍处于打开状态时无法重命名/移动它。

谢谢!这在C#中可行,现在我只需要弄清楚在C中FileShare.Delete的等效物:-/ - zxspectrum
1
请参阅http://msdn.microsoft.com/en-us/library/aa363858%28v=vs.85%29.aspx,特别是dwShareMode参数(FILE_SHARE_DELETE)。 - Joe
@Joe:谢谢,我刚刚发现并完成了测试。我已经编辑了C示例。我还需要使用_open_osfhandle,因为该应用程序使用文件描述符。 - zxspectrum

4
这取决于文件是如何被打开的。如果文件被锁定,你就无法写入或重命名它。类似Notepad++的工具是不会锁定打开的文件的。如果你是打开并编辑文件的人,你也可以这样做。

http://balajiramesh.wordpress.com/2008/07/16/using-streamreader-without-locking-the-file-in-c/

本文中的代码展示了如何使用带有FileShare选项的FileStream:
using(FileStream fs = new FileStream(@”c:\test.txt”, FileMode.Open, FileAccess.Read,FileShare.ReadWrite))
{
    StreamReader sr = new StreamReader(fs);
    txtContents.Text = sr.ReadToEnd();
    sr.Close();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接