I'm trying to create a temporary file that will be automatically deleted.
stream = new FileStream(
tmpFilePath,
FileMode.OpenOrCreate,
FileAccess.ReadWrite,
FileShare.ReadWrite,
4096,
FileOptions.DeleteOnClose|FileOptions.RandomAccess
);
This file will be used by a 3rd party API which will also create a FileStream:
stream = new FileStream(
tmpFilePath,
FileMode.Open,
FileAccess.Read,
FileShare.Read);
I think I've tried all possible combination of flags but I always get a "The process cannot access the file 'XXX' because it is being used by another process..."
Am I doing something wrong? Is there a way around?
According to the documentation, yes.
http://msdn.microsoft.com/en-us/library/system.io.fileshare.aspx
Excerpt:
Read: Allows subsequent opening of the file for reading. If this flag is not specified, any request to open the file for reading (by this process or another process) will fail until the file is closed. However, even if this flag is specified, additional permissions might still be needed to access the file.
I have exactly the same use case and encounter the same problem. What I try is using (FileShare.ReadWrite | FileShare.Delete) for both streams and it works.
It sounds as if you may want to use a memory mapped file as a method to share a single file with multiple processes.
http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile.aspx
The problem is that you still have the first stream you created open. You need to create the file, then release it (close stream), then have the 3rd party API do it's work, then delete the file. Wrapping all this up in a class that is IDispoable might be a nice solution; create and release the file in the contructor, method wrap the 3rd party work, delete in the dispose method.
You can pass existing stream to 3-rd party Api, or if you want only read only mode for 3-rd party Api pass StreamReader
instance
using (var stream = new FileStream("trace.txt", FileMode.OpenOrCreate,FileAccess.ReadWrite))
{
using (var anotherStream = new StreamReader(stream))
{
//magic here
}
}
This sequence of calls will only work if the third party API uses FileShare.ReadWrite
, or your open uses FileAccess.Read
.
You are opening it read/write, while allowing others to also open it read/write.
The third-party code is trying to open it read-only, while allowing others to also have it open, but only as read-only. Since you still have it open read-write, this fails.
Assuming that you can't change the third-party code, you will need to adopt the following pattern instead:
- Open the file as you currently are, but without the
DeleteOnClose
flag.
- Write any content that you need the other code to read.
- Close the file.
- Optionally reopen it with
FileAccess.Read
(and possibly DeleteOnClose
).
- Call the third party code.
- Do any other reading (but not writing) that you want.
In my experience, a FileStream
opened with FileOptions.DeleteOnClose
cannot be opened by passing the file path to another FileStream
regardless of the FileShare
value.
When you own all the code (clearly not your case, sorry) DuplicateHandle can be used to open a DeleteOnClose
file multiple times, even from different processes.
Here's some example code for .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());
}
}
}
}