Ошибка при открытии MemoryMappedFile в другом процессе, компактный каркас C #

Я создал MemoryMappedFile, используя C # .Net Compact Framework на WinCE7.
Когда я пытаюсь открыть тот же MemorymappedFile в другом процессе, я получаю нулевой дескриптор файла.

Вот код, который я использую.

namespace MMFWriteDemo
{
[Serializable]
public class FileMapIOException : IOException
{

private int m_win32Error;
public int Win32ErrorCode
{
get { return m_win32Error; }
}
public override string Message
{
get
{
if (Win32ErrorCode != 0)
return base.Message + " (" + Win32ErrorCode + ")";

return base.Message;
}
}

public FileMapIOException(int error)
: base()
{
m_win32Error = error;
}
public FileMapIOException(string message)
: base(message)
{
}
public FileMapIOException(string message, Exception innerException)
: base(message, innerException)
{
}

} // class FileMapIOException

public enum MapAccess
{
FileMapCopy = 0x0001,
FileMapWrite = 0x0002,
FileMapRead = 0x0004,
FileMapAllAccess = 0x001f,
}

[Flags]
public enum MapProtection
{
PageNone = 0x00000000,
// protection - mutually exclusive, do not or
PageReadOnly = 0x00000002,
PageReadWrite = 0x00000004,
PageWriteCopy = 0x00000008,
// attributes - or-able with protection
SecImage = 0x01000000,
SecReserve = 0x04000000,
SecCommit = 0x08000000,
SecNoCache = 0x10000000,
}

internal class Win32MapApis
{
[DllImport("coredll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr CreateFile(
String lpFileName, int dwDesiredAccess, int dwShareMode,
IntPtr lpSecurityAttributes, int dwCreationDisposition,
int dwFlagsAndAttributes, IntPtr hTemplateFile);

[DllImport("coredll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr CreateFileMapping(
IntPtr hFile, IntPtr lpAttributes, int flProtect,
int dwMaximumSizeLow, int dwMaximumSizeHigh,
String lpName);

[DllImport("coredll", SetLastError = true)]
public static extern bool FlushViewOfFile(
IntPtr lpBaseAddress, IntPtr dwNumBytesToFlush);

[DllImport("coredll", SetLastError = true)]
public static extern IntPtr MapViewOfFile(
IntPtr hFileMappingObject, int dwDesiredAccess, int dwFileOffsetHigh,
int dwFileOffsetLow, IntPtr dwNumBytesToMap);

//[DllImport("coredll", SetLastError = true, CharSet = CharSet.Auto)]
//public static extern IntPtr OpenFileMapping(
//   int dwDesiredAccess, bool bInheritHandle, String lpName);

public static IntPtr OpenFileMapping(uint dwDesiredAccess, bool bInheritHandle, string lpName)
{
IntPtr t_pHandle = Win32MapApis.CreateFileMapping(new IntPtr(-1), IntPtr.Zero, (int)MapAccess.FileMapRead, 0, 0, lpName);
return t_pHandle;
}

[DllImport("coredll", SetLastError = true)]
public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);

[DllImport("coredll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("coredll.dll", SetLastError = true)]
public static extern Int32 GetLastError();
} // class Win32MapApis

public class MemoryMappedFile : MarshalByRefObject, IDisposable
{
//! handle to MemoryMappedFile object
private IntPtr _hMap = IntPtr.Zero;
private MapProtection _protection = MapProtection.PageNone;

private string _fileName = "";
public string FileName { get { return _fileName; } }

private long _maxSize;
private readonly bool _is64bit;

public long MaxSize { get { return _maxSize; } }

#region Constants

private const int GENERIC_READ = unchecked((int)0x80000000);
private const int GENERIC_WRITE = unchecked((int)0x40000000);
private const int OPEN_ALWAYS = 4;
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private static readonly IntPtr NULL_HANDLE = IntPtr.Zero;

#endregion // Constants

#region Properties
public bool IsOpen
{
get { return (_hMap != NULL_HANDLE); }
}

public bool Is64bit
{
get { return _is64bit; }
}

private MemoryMappedFile()
{
_is64bit = IntPtr.Size == 8;
}

~MemoryMappedFile()
{
Dispose(false);
}

#region Create Overloadspublic static MemoryMappedFile
Create(MapProtection protection, long maxSize, string name)
{
return Create(null, protection, maxSize, name);
}public static MemoryMappedFile
Create(MapProtection protection, long maxSize)
{
return Create(null, protection, maxSize, null);
}public static MemoryMappedFile
Create(string fileName, MapProtection protection)
{
return Create(fileName, protection, 0, null);
}public static MemoryMappedFile
Create(string fileName, MapProtection protection,
long maxSize)
{
return Create(fileName, protection, maxSize, null);
}public static MemoryMappedFile
Create(string fileName, MapProtection protection,
long maxSize, String name)
{
MemoryMappedFile map = new MemoryMappedFile();
if (!map.Is64bit && maxSize > uint.MaxValue)
throw new ConstraintException("32bit systems support max size of 4gb.");

// open file first
IntPtr hFile = INVALID_HANDLE_VALUE;

if (!string.IsNullOrEmpty(fileName))
{
if (maxSize == 0)
{
if (!File.Exists(fileName))
{
throw new Exception(string.Format("Winterdom.IO.FileMap.MemoryMappedFile.Create - \"{0}\" does not exist ==> Unable to map entire file", fileName));
}

FileInfo backingFileInfo = new FileInfo(fileName);
maxSize = backingFileInfo.Length;

if (maxSize == 0)
{
throw new Exception(string.Format("Winterdom.IO.FileMap.MemoryMappedFile.Create - \"{0}\" is zero bytes ==> Unable to map entire file", fileName));
}
}

// determine file access needed
// we'll always need generic read access
int desiredAccess = GENERIC_READ;
if ((protection == MapProtection.PageReadWrite) ||
(protection == MapProtection.PageWriteCopy))
{
desiredAccess |= GENERIC_WRITE;
}

// open or create the file
// if it doesn't exist, it gets created
hFile = Win32MapApis.CreateFile(
fileName, desiredAccess, 0,
IntPtr.Zero, OPEN_ALWAYS, 0, IntPtr.Zero
);
if (hFile == INVALID_HANDLE_VALUE)
//throw new FileMapIOException(Marshal.GetHRForLastWin32Error());
throw new FileMapIOException("MMF");

map._fileName = fileName;
}map._hMap = Win32MapApis.CreateFileMapping(
hFile, IntPtr.Zero, (int)protection,
(int)((maxSize >> 32) & 0xFFFFFFFF),
(int)(maxSize & 0xFFFFFFFF), "unique");
// close file handle, we don't need it
if (hFile != INVALID_HANDLE_VALUE) Win32MapApis.CloseHandle(hFile);
if (map._hMap == NULL_HANDLE)
//throw new FileMapIOException(Marshal.GetHRForLastWin32Error());
throw new FileMapIOException("MMF");

map._protection = protection;
map._maxSize = maxSize;

return map;
}

#endregion // Create Overloadspublic static MemoryMappedFile Open(MapAccess access, String name)
{
MemoryMappedFile map = new MemoryMappedFile
{
_hMap = Win32MapApis.OpenFileMapping((uint)access, false, name)
};

if (map._hMap == NULL_HANDLE)
throw new FileMapIOException("MMF");

//throw new FileMapIOException(Marshal.GetHRForLastWin32Error());
map._maxSize = -1; // debug unknown
return map;
}public void Close()
{
Dispose(true);
}

public IntPtr MapView(MapAccess access, long offset, long size)
{
if (!IsOpen)
throw new ObjectDisposedException("Winterdom.IO.FileMap.MemoryMappedFile.MapView - MMF already closed");

// Throws OverflowException if (a) this is a 32-bit platform AND (b) size is out of bounds (ie. int bounds) with respect to this platform
IntPtr mapSize = new IntPtr(size);

IntPtr baseAddress = Win32MapApis.MapViewOfFile(
_hMap, (int)access,
(int)((offset >> 32) & 0xFFFFFFFF),
(int)(offset & 0xFFFFFFFF), mapSize
);

if (baseAddress == IntPtr.Zero)
throw new FileMapIOException("MMF");

//throw new FileMapIOException(Marshal.GetHRForLastWin32Error());

return baseAddress;

}

public MapViewStream MapAsStream()
{
if (!IsOpen)
throw new ObjectDisposedException("Winterdom.IO.FileMap.MemoryMappedFile.MapView - MMF already closed");

// sws should verify against _protection
// Don't know what to do about FILE_MAP_COPY et al

bool isWriteable = (_protection & MapProtection.PageReadWrite) == MapProtection.PageReadWrite;
return new MapViewStream(this, MaxSize, isWriteable);

}

public void UnMapView(IntPtr mapBaseAddr)
{
Win32MapApis.UnmapViewOfFile(mapBaseAddr);
}

public void UnMapView(MapViewStream mappedViewStream)
{
UnMapView(mappedViewStream.ViewBaseAddr);
}

public void Flush(IntPtr viewBaseAddr)
{
// Throws OverflowException if (a) this is a 32-bit platform AND (b) size is out of bounds (ie. int bounds) with respect to this platform
IntPtr flushLength = new IntPtr(MaxSize);
Win32MapApis.FlushViewOfFile(viewBaseAddr, flushLength);
}

public void Flush(MapViewStream mappedViewStream)
{
Flush(mappedViewStream.ViewBaseAddr);
}

#region IDisposable implementation

public void Dispose()
{
Dispose(true);
}

protected virtual void Dispose(bool disposing)
{
if (IsOpen)
Win32MapApis.CloseHandle(_hMap);
_hMap = NULL_HANDLE;

if (disposing)
GC.SuppressFinalize(this);
}

#endregion // IDisposable implementation

}  // class MemoryMappedFile

public class MapViewStream : Stream, IDisposable
{
#region Map/View Related Fields

protected MemoryMappedFile _backingFile;
protected MapAccess _access = MapAccess.FileMapWrite;
protected bool _isWriteable;
IntPtr _viewBaseAddr = IntPtr.Zero; // Pointer to the base address of the currently mapped view
protected long _mapSize;
protected long _viewStartIdx = -1;
protected long _viewSize = -1;
long _position; //! our current position in the stream buffer#region Properties
public IntPtr ViewBaseAddr
{
get { return _viewBaseAddr; }
}
public bool IsViewMapped
{
get { return (_viewStartIdx != -1) && (_viewStartIdx + _viewSize) <= (_mapSize); }
}

#endregion

#endregion // Map/View Related Fields

#region Map / Unmap View

#region Unmap View

protected void UnmapView()
{
if (IsViewMapped)
{
_backingFile.UnMapView(this);
_viewStartIdx = -1;
_viewSize = -1;
}
}

#endregion

#region Map View

protected void MapView(ref long viewStartIdx, ref long viewSize)
{
// Now map the view
_viewBaseAddr = _backingFile.MapView(_access, viewStartIdx, viewSize);
_viewStartIdx = viewStartIdx;
_viewSize = viewSize;

}
#endregion

#endregion

#region Constructorsinternal MapViewStream(MemoryMappedFile backingFile, long mapSize, bool isWriteable)
{
if (backingFile == null)
{
throw new Exception("MapViewStream.MapViewStream - backingFile is null");
}
if (!backingFile.IsOpen)
{
throw new Exception("MapViewStream.MapViewStream - backingFile is not open");
}
if ((mapSize < 1) || (mapSize > backingFile.MaxSize))
{
throw new Exception(string.Format("MapViewStream.MapViewStream - mapSize is invalid.  mapSize == {0}, backingFile.MaxSize == {1}", mapSize, backingFile.MaxSize));
}

_backingFile = backingFile;
_isWriteable = isWriteable;
_access = isWriteable ? MapAccess.FileMapWrite : MapAccess.FileMapRead;
// Need a backingFile.SupportsAccess function that takes a MapAccess compares it against its stored MapProtection protection and returns bool
_mapSize = mapSize;

_isOpen = true;

// Map the first view

Seek(0, SeekOrigin.Begin);
}

#endregion

#region Stream Properties

public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return _isWriteable; }
}
public override long Length
{
get { return _mapSize; }
}

public override long Position
{
get { return _position; }
set { Seek(value, SeekOrigin.Begin); }
}

#endregion // Stream Properties

#region Stream Methods

public override void Flush()
{
if (!IsOpen)
throw new ObjectDisposedException("Winterdom.IO.FileMap.MapViewStream.Flush - Stream is closed");

// flush the view but leave the buffer intact
_backingFile.Flush(this);
}

public override int Read(byte[] buffer, int offset, int count)
{
if (!IsOpen)
throw new ObjectDisposedException("Stream is closed");

if (buffer.Length - offset < count)
throw new ArgumentException("Invalid Offset");

int bytesToRead = (int)Math.Min(Length - _position, count);
Marshal.Copy((IntPtr)(_viewBaseAddr.ToInt64() + _position), buffer, offset, bytesToRead);

_position += bytesToRead;
return bytesToRead;
}

public override void Write(byte[] buffer, int offset, int count)
{
if (!IsOpen)
throw new ObjectDisposedException("Stream is closed");
if (!CanWrite)
throw new FileMapIOException("Stream cannot be written to");

if (buffer.Length - offset < count)
throw new ArgumentException("Invalid Offset");

int bytesToWrite = (int)Math.Min(Length - _position, count);
if (bytesToWrite == 0)
return;

Marshal.Copy(buffer, offset, (IntPtr)(_viewBaseAddr.ToInt64() + _position), bytesToWrite);

_position += bytesToWrite;
}

public override long Seek(long offset, SeekOrigin origin)
{
if (!IsOpen)
throw new ObjectDisposedException("Stream is closed");

long newpos = 0;
switch (origin)
{
case SeekOrigin.Begin: newpos = offset; break;
case SeekOrigin.Current: newpos = Position + offset; break;
case SeekOrigin.End: newpos = Length + offset; break;
}
// sanity check
if (newpos < 0 || newpos > Length)
throw new FileMapIOException("Invalid Seek Offset");
_position = newpos;

if (!IsViewMapped)
{
MapView(ref newpos, ref _mapSize); // use _mapsize here??
}

return newpos;
}

public override void SetLength(long value)
{
// not supported!
throw new NotSupportedException("Winterdom.IO.FileMap.MapViewStream.SetLength - Can't change map size");
}

public override void Close()
{
Dispose(true);
}

#endregion // Stream methods

#region IDisposable Implementation

private bool _isOpen;
public bool IsOpen { get { return _isOpen; } }

public new void Dispose()
{
Dispose(true);
}

protected new virtual void Dispose(bool disposing)
{
if (IsOpen)
{
Flush();
UnmapView();
_isOpen = false;
}

if (disposing)
GC.SuppressFinalize(this);
}

~MapViewStream()
{
Dispose(false);
}

#endregion // IDisposable Implementation

} // class MapViewStream

public class GenericMemoryMappedArray<TValue> : IDisposable, IEnumerable<TValue>
where TValue : struct
{
#region Private fields
private string _path;
private string _fileName;
private string _uniqueName = "mmf-" + "12345Test";//Guid.NewGuid();
private long _fileSize;
private MemoryMappedFile _map;
private int _dataSize;
private bool _deleteFile = true;
private byte[] _buffer;
private IntPtr _memPtr;
private bool _autogrow = true;

private Dictionary<int, MapViewStream> _inUse = new Dictionary<int, MapViewStream>(10);
private Dictionary<int, DateTime> _lastUsedThread = new Dictionary<int, DateTime>();
private readonly object _lockObject = new object();
//private Timer _pooltimer;
private bool _isDisposed;
#endregion

#region Properties

public string UniqueName
{
get { return _uniqueName; }
set { _uniqueName = value; }
}public long Length
{
get
{
return _fileSize / _dataSize;
}
}public long Position
{
set
{
int threadId = Thread.CurrentThread.ManagedThreadId;
_lastUsedThread[threadId] = DateTime.UtcNow;

Stream s = GetView(threadId);
s.Position = value * _dataSize;
}
}public bool AutoGrow
{
get { return _autogrow; }
set { _autogrow = value; }
}

public override string ToString()
{
return string.Format("Length {0}", Length);
}
#endregion

#region Constructor

public GenericMemoryMappedArray(long size, string path)
{
_path = path;

_fileName = Path.Combine(path, _uniqueName + ".bin");
//_fileName = Path.Combine(path, "mmfTest.bin");

// Get the size of TValue
_dataSize = Marshal.SizeOf(typeof(TValue));

// Allocate a global buffer for this instance
_buffer = new byte[_dataSize];
// Allocate a global unmanaged buffer for this instance
_memPtr = Marshal.AllocHGlobal(_dataSize);
SetFileSize(size);
}
#endregion#region Finalizer
~GenericMemoryMappedArray()
{
Dispose(false);
}
#endregion

#region Private methods

private Stream GetView(int threadId)
{
MapViewStream s;
if (!_inUse.TryGetValue(threadId, out s))
{
// create new view and add to pool
MapViewStream mvs = _map.MapAsStream();
lock (_lockObject)
{
_inUse.Add(threadId, mvs);
}
return mvs;
}
return s;
}private void SetFileSize(long size)
{
_fileSize = _dataSize * size;
_map = MemoryMappedFile.Create(_fileName, MapProtection.PageReadWrite, _fileSize);
}
#endregion

#region Public methods
public void Write(byte[] buffer)
{
int threadId = Thread.CurrentThread.ManagedThreadId;
_lastUsedThread[threadId] = DateTime.UtcNow;

Stream s = GetView(threadId);
s.Write(buffer, 0, buffer.Length);
}

public void WriteByte(byte b)
{
int threadId = Thread.CurrentThread.ManagedThreadId;
_lastUsedThread[threadId] = DateTime.UtcNow;

Stream s = GetView(threadId);
byte[] buffer = new byte[1] { b };
s.Write(buffer, 0, 1);
}

public int Read()
{
int threadId = Thread.CurrentThread.ManagedThreadId;
_lastUsedThread[threadId] = DateTime.UtcNow;

Stream s = GetView(threadId);
int count = s.Read(_buffer, 0, _buffer.Length);
return count;
}

public byte ReadByte()
{
int threadId = Thread.CurrentThread.ManagedThreadId;
Stream s = GetView(threadId);

return (byte)s.ReadByte();
}

public TValue this[long index]
{
get
{
lock (this)
{
if (index >= Length)
{
throw new ArgumentOutOfRangeException("index", "Tried to access item outside the array boundaries");
}
Position = index;
Read();
TValue value = ConvertToTValue();
return value;
}
}
set
{
lock (this)
{
if (index >= Length)
{
if (_autogrow)
Grow(index, 10);
else
{
throw new ArgumentOutOfRangeException("index", "Tried to access item outside the array");
}
}
Position = index;
ConvertToBytes(value);
Write(_buffer);
}
}
}

private void ConvertToBytes(TValue value)
{
// Could set the last parameter to false if TValue only contains value types
// Safer to leave it to true for all purposes.
Marshal.StructureToPtr(value, _memPtr, true);
Marshal.Copy(_memPtr, _buffer, 0, _dataSize);
}

private TValue ConvertToTValue()
{
Marshal.Copy(_buffer, 0, _memPtr, _dataSize);

object obj = Marshal.PtrToStructure(_memPtr, typeof(TValue));
return (TValue)obj;
}private void Grow(long size, int percentage)
{
_deleteFile = false;

lock (_lockObject)
{
Dispose(true);
long oldSize = _fileSize;
_fileSize = (long)((float)size * _dataSize * ((100F + percentage) / 100F)); //required filesize
if (_fileSize < (oldSize + _dataSize))
{
_fileSize = oldSize + _dataSize;
}
_map = MemoryMappedFile.Create(_fileName, MapProtection.PageReadWrite, _fileSize);
}
}
#endregion

#region Clone Members

public GenericMemoryMappedArray<TValue> Clone()
{
string copyName = _uniqueName + Guid.NewGuid();
string currentPath = Path.Combine(_path, copyName + ".bin");

File.Copy(_fileName, currentPath);
GenericMemoryMappedArray<TValue> current = new GenericMemoryMappedArray<TValue>(Length, currentPath);
return current;
}
#endregion

#region IDisposable Members

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (!_isDisposed && disposing)
{
lock (_lockObject)
{
// Clean up all views
foreach (KeyValuePair<int, MapViewStream> pair in _inUse)
{
pair.Value.Dispose();
pair.Value.Close();
}
_inUse.Clear();
_lastUsedThread.Clear();
}

if (_map != null)
{
_map.Close();
}
}

try
{
if (_deleteFile)
{
Marshal.DestroyStructure(_memPtr, typeof(TValue)); // Clear unmanaged buffer data
Marshal.FreeHGlobal(_memPtr); // Free unmanaged buffer

if (File.Exists(_fileName)) File.Delete(_fileName);
}
}
catch (Exception)
{
// TODO: Handle files which for some reason didn't want to be deleted
throw;
}
_deleteFile = true;
}
#endregion

#region IEnumerable<TValue> Members

public IEnumerator<TValue> GetEnumerator()
{
lock (this)
{
Position = 0;
for (int i = 0; i < Length; i++)
{
Read();
yield return ConvertToTValue();
}
}
}

#endregion

IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}

}

Приложение для создания файла сопоставления памяти и записи в него, это работает хорошо.

namespace MMFWriteDemo
{
class Program
{
static void Main(string[] args)
{
GenericMemoryMappedArray<byte> mmfTest = new GenericMemoryMappedArray<byte>(1024 * 1024 * 8, @"internal\data");
int i = 0;
byte b = 0;
while (i < 1000)
{
mmfTest.WriteByte(b);
b++;
if (b == 255)
{
b = 0;
}
i++;
}
Console.ReadLine();
}
}
}

Это хорошо работает, я вижу созданный файл и могу писать в него.

Приложение для открытия файла памяти и чтения из него, оно не работает.

namespace MMFReaderDemo
{
class Program
{
static void Main(string[] args)
{
GenericMemoryMappedArray<byte> mmfTest = new GenericMemoryMappedArray<byte>(1024 * 1024 * 8, @"internal\data");
int i = 0;
while (i < 100)
{
Console.WriteLine(mmfTest.ReadByte());
i++;
}
}
}
}

ErrorCode: 0x80000005

На самом деле я пытаюсь разделить файл отображения памяти между драйвером (написанным на C ++) и пользовательским приложением (написанным на C #).

1

Решение

Задача ещё не решена.

Другие решения