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

时间:2011-11-21 15:52:56

标签: c# .net filestream file-sharing

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

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',因为它正被另一个进程使用......”

我做错了吗?有办法吗?

7 个答案:

答案 0 :(得分:3)

根据文件,是的。

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

摘录:

读取:允许随后打开文件进行读取。如果未指定此标志,则在文件关闭之前,任何打开文件以进行读取(此进程或其他进程)的请求都将失败。但是,即使指定了此标志,仍可能需要其他权限才能访问该文件。

答案 1 :(得分:2)

我有完全相同的用例并遇到同样的问题。我尝试使用(FileShare.ReadWrite | FileShare.Delete)两个流,它的工作原理。

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

问题是您仍然拥有您创建的第一个流。您需要创建该文件,然后释放它(关闭流),然后让第三方API执行它,然后删除该文件。将所有这些包装在IDispoable的类中可能是一个很好的解决方案;在构造函数中创建和释放文件,方法包装第三方工作,在dispose方法中删除。

答案 4 :(得分:0)

您可以将现有流传递到第3方Api,或者如果您只想要第3方Api传递StreamReader实例

的只读模式
    using (var stream = new FileStream("trace.txt", FileMode.OpenOrCreate,FileAccess.ReadWrite))
    {
        using (var anotherStream = new StreamReader(stream))
        {
            //magic here
        }
    }

答案 5 :(得分:0)

这一系列调用只有在第三方API使用FileShare.ReadWrite或您的公开使用FileAccess.Read时才有效。

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

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

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

答案 6 :(得分:0)

根据我的经验,无论FileStream值如何,都无法通过将文件路径传递给其他FileOptions.DeleteOnClose来打开FileStream FileShare {/ 1}}。

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