在同一进程中的同一文件上创建2个FileStream

我正在尝试创建一个将自动删除的临时文件。

stream = new FileStream( tmpFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, FileOptions.DeleteOnClose|FileOptions.RandomAccess ); 

此文件将由第三方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)两个流,它的工作原理。

听起来好像您可能希望使用内存映射文件作为与多个进程共享单个文件的方法。

http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile.aspx

问题是您仍然拥有您创建的第一个流。 您需要创建该文件,然后释放它(关闭流),然后让第三方API执行它,然后删除该文件。 将所有这些包装在IDispoable的类中可能是一个很好的解决方案; 在构造函数中创建和释放文件,方法包装第三方工作,在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时才有效,或者您的open使用FileAccess.Read

您正在打开它的读/写,同时允许其他人也打开它读/写。 第三方代码试图以只读方式打开它,同时允许其他人也打开它,但只能以只读方式打开。 由于您仍然可以打开读写,因此会失败。

假设您无法更改第三方代码,则需要采用以下模式:

  1. 按照您当前的方式打开文件,但没有DeleteOnClose标志。
  2. 写下您需要阅读其他代码的任何内容。
  3. 关闭文件。
  4. (可选)使用FileAccess.Read (可能还有DeleteOnClose )重新打开它。
  5. 调用第三方代码。
  6. 做你想要的任何其他阅读(但不是写作)。

根据我的经验,无论FileShare值如何,都无法通过将文件路径传递给另一个FileStream来打开使用FileOptions.DeleteOnClose打开的FileStream

当您拥有所有代码时(显然不是您的情况,很抱歉) 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()); } } } }