8

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

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

难道我做错了什么?有办法吗?

4

7 回答 7

4

根据文档,是的。

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

摘抄:

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

于 2011-11-21T16:07:15.013 回答
2

我有完全相同的用例并遇到同样的问题。我尝试对两个流使用 (FileShare.ReadWrite | FileShare.Delete) 并且它有效。

于 2015-11-04T18:42:59.577 回答
1

以我的经验,无论值如何FileStreamFileOptions.DeleteOnClose都无法通过将文件路径传递给另一个来打开打开的文件。FileStreamFileShare

当您拥有所有代码时(显然不是您的情况,抱歉)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());
      }
    }
  }
}
于 2016-04-13T18:10:27.007 回答
0

StreamReader您可以将现有流传递给 3-rd 方 Api,或者如果您想要 3-rd 方 Api 传递实例的只读模式

    using (var stream = new FileStream("trace.txt", FileMode.OpenOrCreate,FileAccess.ReadWrite))
    {
        using (var anotherStream = new StreamReader(stream))
        {
            //magic here
        }
    }
于 2011-11-21T16:07:55.533 回答
0

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

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

于 2011-11-21T16:02:42.510 回答
0

问题是您仍然打开了您创建的第一个流。您需要创建文件,然后释放它(关闭流),然后让第 3 方 API 完成它的工作,然后删除文件。将所有这些封装在一个 IDispoable 类中可能是一个不错的解决方案;在contructor中创建和释放文件,方法包裹第3方工作,在dispose方法中删除。

于 2011-11-21T16:04:00.120 回答
0

FileShare.ReadWrite只有在第三方 API 使用或您的开放使用时,此调用序列才会起作用FileAccess.Read

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

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

  1. 按当前方式打开文件,但不带DeleteOnClose标志。
  2. 编写您需要其他代码阅读的任何内容。
  3. 关闭文件。
  4. FileAccess.Read可以选择使用(并且可能)重新打开它DeleteOnClose
  5. 调用第三方代码。
  6. 做你想做的任何其他阅读(但不是写作)。
于 2015-12-09T03:30:05.973 回答