我正在尝试创建一个将自动删除的临时文件。
stream = new FileStream(
tmpFilePath,
FileMode.OpenOrCreate,
FileAccess.ReadWrite,
FileShare.ReadWrite,
4096,
FileOptions.DeleteOnClose|FileOptions.RandomAccess
);
此文件将由第 3 方 API 使用,该 API 还将创建 FileStream:
stream = new FileStream(
tmpFilePath,
FileMode.Open,
FileAccess.Read,
FileShare.Read);
我想我已经尝试了所有可能的标志组合,但我总是得到“该进程无法访问文件‘XXX’,因为它正在被另一个进程使用......”
我做错了什么吗?有办法吗?
根据文档,是的。
http://msdn.microsoft.com/en-us/library/system.io.fileshare.aspx
摘录:
读取:允许随后打开文件进行读取。如果未指定此标志,则任何打开文件进行读取的请求(此进程或其他进程)都将失败,直到文件关闭为止。但是,即使指定了此标志,可能仍然需要额外的权限才能访问该文件。
我有完全相同的用例并遇到同样的问题。我尝试对两个流使用 (FileShare.ReadWrite | FileShare.Delete) 并且它有效。
更新:这不再是一个很好的答案,但仍然是一个有趣的答案,并且可能仍然适用于旧版本的 .NET。更好的现代答案是指定 FileShare.ReadWrite |创建第二个流时的 FileShare.Delete 选项。请参阅https://github.com/dotnet/runtime/issues/91484
原始答案:根据我的经验,无论
FileStream
值如何,都无法通过将文件路径传递给另一个FileOptions.DeleteOnClose
来打开用FileStream
打开的FileShare
。
当您拥有所有代码时(显然不是您的情况,抱歉)DuplicateHandle可用于多次打开
DeleteOnClose
文件,甚至可以从不同的进程打开。
这是 .NET 4.5.1 的一些示例代码。
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32.SafeHandles;
namespace Example
{
public static class DuplicatedHandleExample
{
[DllImport("kernel32.dll")]
private static extern bool DuplicateHandle(
SafeFileHandle hSourceProcessHandle,
IntPtr hSourceHandle,
SafeFileHandle hTargetProcessHandle,
out SafeFileHandle lpTargetHandle,
UInt32 dwDesiredAccess,
bool bInheritHandle,
UInt32 dwOptions);
[DllImport("kernel32.dll")]
private static extern SafeFileHandle OpenProcess(
UInt32 dwDesiredAccess,
bool bInheritHandle,
int dwProcessId);
private const UInt32 PROCESS_DUP_HANDLE = 0x0040;
private const UInt32 DUPLICATE_SAME_ACCESS = 0x0002;
public static void CreateFileInProcessA()
{
try
{
// open new temp file with FileOptions.DeleteOnClose
string tempFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("D"));
using (FileStream fs = new FileStream(tempFilePath, FileMode.CreateNew,
FileAccess.ReadWrite, FileShare.Read | FileShare.Write | FileShare.Delete,
4096, FileOptions.DeleteOnClose))
{
// put a message in the temp file
fs.Write(new[] { (byte)'h', (byte)'i', (byte)'!' }, 0, 3);
fs.Flush();
// put our process ID and file handle on clipboard
string data = string.Join(",",
Process.GetCurrentProcess().Id.ToString(),
fs.SafeFileHandle.DangerousGetHandle().ToString());
Clipboard.SetData(DataFormats.UnicodeText, data);
// show messagebox (while holding file open!) and wait for user to click OK
MessageBox.Show("Temp File opened. Process ID and File Handle copied to clipboard. Click OK to close temp file.");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
public static void OpenFileInProcessB()
{
try
{
// get process ID and file handle from clipboard
string data = (string)Clipboard.GetData(DataFormats.UnicodeText);
string[] dataParts = data.Split(',');
int sourceProcessId = int.Parse(dataParts[0]);
IntPtr sourceFileHandle = new IntPtr(Int64.Parse(dataParts[1]));
// get handle to target process
using (SafeFileHandle sourceProcessHandle =
OpenProcess(PROCESS_DUP_HANDLE, false, sourceProcessId))
{
// get handle to our process
using (SafeFileHandle destinationProcessHandle =
OpenProcess(PROCESS_DUP_HANDLE, false, Process.GetCurrentProcess().Id))
{
// duplicate handle into our process
SafeFileHandle destinationFileHandle;
DuplicateHandle(sourceProcessHandle, sourceFileHandle,
destinationProcessHandle, out destinationFileHandle,
0, false, DUPLICATE_SAME_ACCESS);
// get a FileStream wrapper around it
using (FileStream fs = new FileStream(destinationFileHandle, FileAccess.ReadWrite, 4096))
{
// read file contents
fs.Position = 0;
byte[] buffer = new byte[100];
int numBytes = fs.Read(buffer, 0, 100);
string message = Encoding.ASCII.GetString(buffer, 0, numBytes);
// show messagebox (while holding file open!) and wait for user to click OK
MessageBox.Show("Found this message in file: " + message + Environment.NewLine +
"Click OK to close temp file");
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
听起来好像您可能想使用内存映射文件作为与多个进程共享单个文件的方法。
http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile.aspx
问题是您创建的第一个流仍然处于打开状态。 您需要创建文件,然后释放它(关闭流),然后让第 3 方 API 完成工作,然后删除文件。 将所有这些包装在 IDispoable 类中可能是一个不错的解决方案;在构造函数中创建并释放文件,方法包装第 3 方工作,在 dispose 方法中删除。
您可以将现有流传递给第 3 方 Api,或者如果您只想将第 3 方 Api 的只读模式传递给
StreamReader
实例
using (var stream = new FileStream("trace.txt", FileMode.OpenOrCreate,FileAccess.ReadWrite))
{
using (var anotherStream = new StreamReader(stream))
{
//magic here
}
}
仅当第三方 API 使用
FileShare.ReadWrite
,或者您的开放使用 FileAccess.Read
时,此调用序列才有效。
您以读/写方式打开它,同时允许其他人也以读/写方式打开它。 第三方代码试图以只读方式打开它,同时允许其他人也将其打开,但只能以只读方式打开。 由于您仍然以读写方式打开它,因此失败。
假设您无法更改第三方代码,则需要采用以下模式:
DeleteOnClose
标志。FileAccess.Read
(也可能是 DeleteOnClose
)重新打开它。