Создайте 2 FileStream для того же файла в том же процессе
Я пытаюсь создать временный файл, который будет автоматически удален.
stream = new FileStream(
tmpFilePath,
FileMode.OpenOrCreate,
FileAccess.ReadWrite,
FileShare.ReadWrite,
4096,
FileOptions.DeleteOnClose|FileOptions.RandomAccess
);
Этот файл будет использоваться сторонним API, который также создаст FileStream:
stream = new FileStream(
tmpFilePath,
FileMode.Open,
FileAccess.Read,
FileShare.Read);
Я думаю, что перепробовал все возможные комбинации флагов, но всегда получаю "Процесс не может получить доступ к файлу" XXX ", потому что он используется другим процессом..."
Я делаю что-то неправильно? Есть ли способ обойти?
7 ответов
По документации да.
http://msdn.microsoft.com/en-us/library/system.io.fileshare.aspx
Выдержка:
Чтение: позволяет последующее открытие файла для чтения. Если этот флаг не указан, любой запрос на открытие файла для чтения (этим процессом или другим процессом) не будет выполнен, пока файл не будет закрыт. Однако даже если этот флаг указан, для доступа к файлу могут потребоваться дополнительные разрешения.
У меня точно такой же вариант использования, и я столкнулся с той же проблемой. Я пытаюсь использовать (FileShare.ReadWrite | FileShare.Delete) для обоих потоков, и это работает.
По моему опыту, 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());
}
}
}
}
Вы можете передать существующий поток в сторонний Api, или если вы хотите только режим только для чтения для стороннего прохода Api StreamReader
пример
using (var stream = new FileStream("trace.txt", FileMode.OpenOrCreate,FileAccess.ReadWrite))
{
using (var anotherStream = new StreamReader(stream))
{
//magic here
}
}
Звучит так, как будто вы хотите использовать файл с отображенной памятью в качестве метода для совместного использования одного файла с несколькими процессами.
http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile.aspx
Проблема в том, что у вас все еще открыт первый созданный вами поток. Вам нужно создать файл, затем освободить его (закрыть поток), затем заставить сторонний API выполнить свою работу, а затем удалить файл. Заключение всего этого в класс IDispoable может быть хорошим решением; создайте и отпустите файл в конструкторе, метод оберните сторонней работой, удалите в методе dispose.
Эта последовательность вызовов будет работать, только если сторонний API использует FileShare.ReadWrite
или ваше открытое использование FileAccess.Read
,
Вы открываете его для чтения / записи, одновременно позволяя другим открывать его для чтения / записи. Сторонний код пытается открыть его только для чтения, позволяя другим также открывать его, но только как доступный только для чтения. Поскольку он все еще открыт для чтения и записи, это не удастся.
Предполагая, что вы не можете изменить сторонний код, вам нужно будет принять следующий шаблон:
- Откройте файл как есть, но без
DeleteOnClose
флаг. - Напишите любой контент, который вам нужен для прочтения другого кода.
- Закройте файл.
- При желании снова открыть его
FileAccess.Read
(и, возможно,DeleteOnClose
). - Позвоните стороннему коду.
- Делайте любое другое чтение (но не пишите), которое вы хотите.