RFID RC522 Raspberry PI 2 Windows IOT - c#

I'm looking for a way to use the RFID "RC522" on a Raspberry Pi 2.0 on Windows IOT.
It is of course not offical compatible...
The offical one (OM5577 demo board) is way to expensive in France (i haven't found any reseller who sold it without a lot of shipping cost (total cost is around 80$)).
The RC522 is cheap (<10$). It works great on Arduino and on Raspberry Pi 2.0 on linux. But unfortunatly not yet on Windows IOT.
I'm actually using an arduino as a bridge... It isn't an optimal solution; but work well and cost always half the price than the OM5577.
I've found this project and try to convert them into a VS (Visual C++) project with the Windows IOT SIP and IO... I miserably fail...
In my dream I would be able to use this device in C# with the standard windows IOT "ProximityDevice" class.
Have you any idea for me?
Thanks in advance.

I finally found a solution.
I haven't success in the arudino portability; so i've used this project as start point.
The project is writen in C#. I've just adapt the code for Windows IOT GPIO and SPI. An it's working !
Main
var mfrc = new Mfrc522();
await mfrc.InitIO();
while (true)
{
if (mfrc.IsTagPresent())
{
var uid = mfrc.ReadUid();
mfrc.HaltTag();
}
}
Library Mfrc522Lib.cs (all in one)
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.Gpio;
using Windows.Devices.Spi;
namespace Mfrc522Lib
{
public static class Registers
{
private const byte bitFraming = 0x0D;
private const byte comIrq = 0x04;
private const byte comIrqEnable = 0x02;
private const byte command = 0x01;
private const byte control = 0x0C;
private const byte error = 0x06;
private const byte fifoData = 0x09;
private const byte fifoLevel = 0x0A;
private const byte mode = 0x11;
private const byte rxMode = 0x13;
private const byte timerMode = 0x2A;
private const byte timerPrescaler = 0x2B;
private const byte timerReloadHigh = 0x2C;
private const byte timerReloadLow = 0x2D;
private const byte txAsk = 0x15;
private const byte txControl = 0x14;
private const byte txMode = 0x12;
private const byte version = 0x37;
public static byte BitFraming
{
get
{
return bitFraming;
}
}
public static byte ComIrq
{
get
{
return comIrq;
}
}
public static byte ComIrqEnable
{
get
{
return comIrqEnable;
}
}
public static byte Command
{
get
{
return command;
}
}
public static byte Control
{
get
{
return control;
}
}
public static byte Error
{
get
{
return error;
}
}
public static byte FifoData
{
get
{
return fifoData;
}
}
public static byte FifoLevel
{
get
{
return fifoLevel;
}
}
public static byte Mode
{
get
{
return mode;
}
}
public static byte RxMode
{
get
{
return rxMode;
}
}
public static byte TimerMode
{
get
{
return timerMode;
}
}
public static byte TimerPrescaler
{
get
{
return timerPrescaler;
}
}
public static byte TimerReloadHigh
{
get
{
return timerReloadHigh;
}
}
public static byte TimerReloadLow
{
get
{
return timerReloadLow;
}
}
public static byte TxAsk
{
get
{
return txAsk;
}
}
public static byte TxControl
{
get
{
return txControl;
}
}
public static byte TxMode
{
get
{
return txMode;
}
}
public static byte Version
{
get
{
return version;
}
}
}
public static class PiccResponses
{
private const ushort answerToRequest = 0x0004;
private const byte selectAcknowledge = 0x08;
private const byte acknowledge = 0x0A;
public static byte Acknowledge
{
get
{
return acknowledge;
}
}
public static byte SelectAcknowledge
{
get
{
return selectAcknowledge;
}
}
public static ushort AnswerToRequest
{
get
{
return answerToRequest;
}
}
}
public static class PiccCommands
{
private const byte anticollision_1 = 0x93;
private const byte anticollision_2 = 0x20;
private const byte authenticateKeyA = 0x60;
private const byte authenticateKeyB = 0x61;
private const byte halt_1 = 0x50;
private const byte halt_2 = 0x00;
private const byte read = 0x30;
private const byte request = 0x26;
private const byte select_1 = 0x93;
private const byte select_2 = 0x70;
private const byte write = 0xA0;
public static byte AuthenticateKeyA
{
get
{
return authenticateKeyA;
}
}
public static byte AuthenticateKeyB
{
get
{
return authenticateKeyB;
}
}
public static byte Halt_1
{
get
{
return halt_1;
}
}
public static byte Halt_2
{
get
{
return halt_2;
}
}
public static byte Read
{
get
{
return read;
}
}
public static byte Request
{
get
{
return request;
}
}
public static byte Select_1
{
get
{
return select_1;
}
}
public static byte Select_2
{
get
{
return select_2;
}
}
public static byte Write
{
get
{
return write;
}
}
public static byte Anticollision_1
{
get
{
return anticollision_1;
}
}
public static byte Anticollision_2
{
get
{
return anticollision_2;
}
}
}
public static class PcdCommands
{
private const byte idle = 0x00;
private const byte mifareAuthenticate = 0x0E;
private const byte transceive = 0x0C;
public static byte Idle
{
get
{
return idle;
}
}
public static byte MifareAuthenticate
{
get
{
return mifareAuthenticate;
}
}
public static byte Transceive
{
get
{
return transceive;
}
}
}
public class Uid
{
public byte Bcc { get; private set; }
public byte[] Bytes { get; private set; }
public byte[] FullUid { get; private set; }
public bool IsValid { get; private set; }
internal Uid(byte[] uid)
{
FullUid = uid;
Bcc = uid[4];
Bytes = new byte[4];
System.Array.Copy(FullUid, 0, Bytes, 0, 4);
foreach (var b in Bytes)
{
if (b != 0x00)
IsValid = true;
}
}
public sealed override bool Equals(object obj)
{
if (!(obj is Uid))
return false;
var uidWrapper = (Uid)obj;
for (int i = 0; i < 5; i++)
{
if (FullUid[i] != uidWrapper.FullUid[i])
return false;
}
return true;
}
public sealed override int GetHashCode()
{
int uid = 0;
for (int i = 0; i < 4; i++)
uid |= Bytes[i] << (i * 8);
return uid;
}
public sealed override string ToString()
{
var formatString = "x" + (Bytes.Length * 2);
return GetHashCode().ToString(formatString);
}
}
public sealed class Mfrc522
{
public SpiDevice _spi { get; private set; }
public GpioController IoController { get; private set; }
public GpioPin _resetPowerDown { get; private set; }
/* Uncomment for Raspberry Pi 2 */
private const string SPI_CONTROLLER_NAME = "SPI0";
private const Int32 SPI_CHIP_SELECT_LINE = 0;
private const Int32 RESET_PIN = 25;
internal async Task InitIO()
{
try
{
IoController = GpioController.GetDefault();
_resetPowerDown = IoController.OpenPin(RESET_PIN);
_resetPowerDown.Write(GpioPinValue.High);
_resetPowerDown.SetDriveMode(GpioPinDriveMode.Output);
}
/* If initialization fails, throw an exception */
catch (Exception ex)
{
throw new Exception("GPIO initialization failed", ex);
}
try
{
var settings = new SpiConnectionSettings(SPI_CHIP_SELECT_LINE);
settings.ClockFrequency = 1000000;
settings.Mode = SpiMode.Mode0;
String spiDeviceSelector = SpiDevice.GetDeviceSelector();
IReadOnlyList<DeviceInformation> devices = await DeviceInformation.FindAllAsync(spiDeviceSelector);
_spi = await SpiDevice.FromIdAsync(devices[0].Id, settings);
}
/* If initialization fails, display the exception and stop running */
catch (Exception ex)
{
throw new Exception("SPI Initialization Failed", ex);
}
Reset();
}
public void Reset()
{
_resetPowerDown.Write(GpioPinValue.Low);
System.Threading.Tasks.Task.Delay(50).Wait();
_resetPowerDown.Write(GpioPinValue.High);
System.Threading.Tasks.Task.Delay(50).Wait();
// Force 100% ASK modulation
WriteRegister(Registers.TxAsk, 0x40);
// Set CRC to 0x6363
WriteRegister(Registers.Mode, 0x3D);
// Enable antenna
SetRegisterBits(Registers.TxControl, 0x03);
}
public bool IsTagPresent()
{
// Enable short frames
WriteRegister(Registers.BitFraming, 0x07);
// Transceive the Request command to the tag
Transceive(false, PiccCommands.Request);
// Disable short frames
WriteRegister(Registers.BitFraming, 0x00);
// Check if we found a card
return GetFifoLevel() == 2 && ReadFromFifoShort() == PiccResponses.AnswerToRequest;
}
public Uid ReadUid()
{
// Run the anti-collision loop on the card
Transceive(false, PiccCommands.Anticollision_1, PiccCommands.Anticollision_2);
// Return tag UID from FIFO
return new Uid(ReadFromFifo(5));
}
public void HaltTag()
{
// Transceive the Halt command to the tag
Transceive(false, PiccCommands.Halt_1, PiccCommands.Halt_2);
}
public bool SelectTag(Uid uid)
{
// Send Select command to tag
var data = new byte[7];
data[0] = PiccCommands.Select_1;
data[1] = PiccCommands.Select_2;
uid.FullUid.CopyTo(data, 2);
Transceive(true, data);
return GetFifoLevel() == 1 && ReadFromFifo() == PiccResponses.SelectAcknowledge;
}
internal byte[] ReadBlock(byte blockNumber, Uid uid, byte[] keyA = null, byte[] keyB = null)
{
if (keyA != null)
MifareAuthenticate(PiccCommands.AuthenticateKeyA, blockNumber, uid, keyA);
else if (keyB != null)
MifareAuthenticate(PiccCommands.AuthenticateKeyB, blockNumber, uid, keyB);
else
return null;
// Read block
Transceive(true, PiccCommands.Read, blockNumber);
return ReadFromFifo(16);
}
internal bool WriteBlock(byte blockNumber, Uid uid, byte[] data, byte[] keyA = null, byte[] keyB = null)
{
if (keyA != null)
MifareAuthenticate(PiccCommands.AuthenticateKeyA, blockNumber, uid, keyA);
else if (keyB != null)
MifareAuthenticate(PiccCommands.AuthenticateKeyB, blockNumber, uid, keyB);
else
return false;
// Write block
Transceive(true, PiccCommands.Write, blockNumber);
if (ReadFromFifo() != PiccResponses.Acknowledge)
return false;
// Make sure we write only 16 bytes
var buffer = new byte[16];
data.CopyTo(buffer, 0);
Transceive(true, buffer);
return ReadFromFifo() == PiccResponses.Acknowledge;
}
protected void MifareAuthenticate(byte command, byte blockNumber, Uid uid, byte[] key)
{
// Put reader in Idle mode
WriteRegister(Registers.Command, PcdCommands.Idle);
// Clear the FIFO
SetRegisterBits(Registers.FifoLevel, 0x80);
// Create Authentication packet
var data = new byte[12];
data[0] = command;
data[1] = (byte)(blockNumber & 0xFF);
key.CopyTo(data, 2);
uid.Bytes.CopyTo(data, 8);
WriteToFifo(data);
// Put reader in MfAuthent mode
WriteRegister(Registers.Command, PcdCommands.MifareAuthenticate);
// Wait for (a generous) 25 ms
System.Threading.Tasks.Task.Delay(25).Wait();
}
protected void Transceive(bool enableCrc, params byte[] data)
{
if (enableCrc)
{
// Enable CRC
SetRegisterBits(Registers.TxMode, 0x80);
SetRegisterBits(Registers.RxMode, 0x80);
}
// Put reader in Idle mode
WriteRegister(Registers.Command, PcdCommands.Idle);
// Clear the FIFO
SetRegisterBits(Registers.FifoLevel, 0x80);
// Write the data to the FIFO
WriteToFifo(data);
// Put reader in Transceive mode and start sending
WriteRegister(Registers.Command, PcdCommands.Transceive);
SetRegisterBits(Registers.BitFraming, 0x80);
// Wait for (a generous) 25 ms
System.Threading.Tasks.Task.Delay(25).Wait();
// Stop sending
ClearRegisterBits(Registers.BitFraming, 0x80);
if (enableCrc)
{
// Disable CRC
ClearRegisterBits(Registers.TxMode, 0x80);
ClearRegisterBits(Registers.RxMode, 0x80);
}
}
protected byte[] ReadFromFifo(int length)
{
var buffer = new byte[length];
for (int i = 0; i < length; i++)
buffer[i] = ReadRegister(Registers.FifoData);
return buffer;
}
protected byte ReadFromFifo()
{
return ReadFromFifo(1)[0];
}
protected void WriteToFifo(params byte[] values)
{
foreach (var b in values)
WriteRegister(Registers.FifoData, b);
}
protected int GetFifoLevel()
{
return ReadRegister(Registers.FifoLevel);
}
protected byte ReadRegister(byte register)
{
register <<= 1;
register |= 0x80;
var writeBuffer = new byte[] { register, 0x00 };
return TransferSpi(writeBuffer)[1];
}
protected ushort ReadFromFifoShort()
{
var low = ReadRegister(Registers.FifoData);
var high = (ushort)(ReadRegister(Registers.FifoData) << 8);
return (ushort)(high | low);
}
protected void WriteRegister(byte register, byte value)
{
register <<= 1;
var writeBuffer = new byte[] { register, value };
TransferSpi(writeBuffer);
}
protected void SetRegisterBits(byte register, byte bits)
{
var currentValue = ReadRegister(register);
WriteRegister(register, (byte)(currentValue | bits));
}
protected void ClearRegisterBits(byte register, byte bits)
{
var currentValue = ReadRegister(register);
WriteRegister(register, (byte)(currentValue & ~bits));
}
private byte[] TransferSpi(byte[] writeBuffer)
{
var readBuffer = new byte[writeBuffer.Length];
_spi.TransferFullDuplex(writeBuffer, readBuffer);
return readBuffer;
}
}
}
Schema

Since it uses SPI, there shouldn't be a hardware compatibility issue. If you don't want to translate existing Arduino code, Microsoft does have some tech that allows you to use existing Arduino sketches and libraries. You can read more about it here: http://ms-iot.github.io/content/en-US/win10/ArduinoWiringProjectGuide.htm

Addition: If you want to see the UID as a string representing the hex chars, you can use the following inside TagIsPresent part:
var uid = mfrc.ReadUid();
string txt_Result = "";
foreach (byte byt in uid.FullUid)
{
txt_Result = txt_Result + byt.ToString("x2");
}
mfrc.HaltTag();

Related

Create Program reads Float Big Endian from game (Cemu) to get position of player

I learned a bit of the basics of C# language a couple years ago and now I'm slowly trying to increase knowledge of it through the creation of a trainer for a CEMU game i really enjoyed (hope i dont get criticized for my honesty).
Since i'm a noob at this i learned much from looking at another trainer that a programer released on gitHub, mostly copying classes and then trying to understand the how and why of things... Surely not the best way to learn but one has to start somewhre. But now i'm way out of my league.
I've managed to get offsets (2byte Big Endian) of many things (HP,Stamina,Speed, Stamina) and am able to chage values. Now i wanted to get player position and though i have right offsets i have no idea how work with Float Big Endian.
My repositary (not all files yet) is here:
https://github.com/Snowfall007/ZELDA-BOTW-Trainer
The class - ProcessWrapper is probably the key, but dont know how to work it.
Any good samaritan with patience and skill to help this kid at heart?
internal class ProcessWrapper
{
private readonly string _processName;
private string _processLocation;
private Kernel32.SafeHPROCESS _process;
private long _baseAddress = 0x0;
public ProcessWrapper(string processName)
{
_processName = processName;
}
public bool OpenProcess()
{
Process[] processes = Process.GetProcessesByName(_processName);
if (processes.Length != 1)
{
return false;
}
// Required rights : 0x0010 | 0x0400 | 0x0020 | 0x0008
_process = Kernel32.OpenProcess(ACCESS_MASK.GENERIC_ALL, true, (uint)processes[0].Id);
if (processes[0].MainModule == null) throw new Exception("Process main module is null.");
_processLocation = processes[0].MainModule.FileName;
return true;
}
public void SetBaseAddress(long baseAddress)
{
_baseAddress = baseAddress;
}
public bool OverwriteMemoryProtection(long address, int size)
{
return Kernel32.VirtualProtectEx(_process, (IntPtr)(_baseAddress + address), size, Kernel32.MEM_PROTECTION.PAGE_READWRITE, out Kernel32.MEM_PROTECTION oldProtection);
}
public byte[] ReadMemory(long address, int size, MemoryType memoryType)
{
IntPtr memoryReadPtr = Marshal.AllocHGlobal(size);
Kernel32.ReadProcessMemory(_process, (IntPtr)(_baseAddress + address), memoryReadPtr, size, out SizeT readSize);
byte[] memoryBuffer = new byte[readSize];
Marshal.Copy(memoryReadPtr, memoryBuffer, 0, readSize);
Marshal.FreeHGlobal(memoryReadPtr);
return ProcessMemoryType(memoryBuffer, memoryType);
}
public byte[] ReadMemory(Offset offset)
{
return ReadMemory(offset.Address, offset.Size, offset.MemoryType);
}
public bool WriteMemory(long address, byte[] data, MemoryType memoryType)
{
data = ProcessMemoryType(data, memoryType);
IntPtr dataPtr = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data, 0, dataPtr, data.Length);
bool writeResult = Kernel32.WriteProcessMemory(_process, (IntPtr)(_baseAddress + address), dataPtr, data.Length, out SizeT wroteCount);
Marshal.FreeHGlobal(dataPtr);
return writeResult;
}
public bool WriteMemory(Offset offset, byte[] data)
{
return WriteMemory(offset.Address, data, offset.MemoryType);
}
public string GetProcessLocation()
{
return _processLocation;
}
private static byte[] ProcessMemoryType(byte[] data, MemoryType memoryType)
{
if (memoryType == MemoryType.BigEndian && data.Length > 1)
{
data = data.Reverse().ToArray();
}
return data;
}
}
//In Form1 class i've have also these instructions:
//Coordinates
//Position
public int GetXPosition()
{
return ReadOffset(GameOffsets.Xposition);
}
public bool UpdateXPosition(int value)
{
return WriteOffset(GameOffsets.Xposition, value);
}
public int GetYPosition()
{
return ReadOffset(GameOffsets.Yposition);
}
public bool UpdateYPosition(int value)
{
return WriteOffset(GameOffsets.Yposition, value);
}
public int GetZPosition()
{
return ReadOffset(GameOffsets.Zposition);
}
public bool UpdateZPosition(int value)
{
return WriteOffset(GameOffsets.Zposition, value);
}
//AND ALSO THIS:
private int ReadOffset(Offset offset)
{
byte[] data = _processWrapper.ReadMemory(offset);
if (data.Length != offset.Size) return -1;
else if (data.Length == 1) return data[0];
else if (data.Length == 2) return BitConverter.ToInt16(data, 0);
throw new Exception("32 Bits and 64 Bits types are currently not handled");
}
How can i make changes to work specifically with Float Big Endian?

How can I load a sound file into memory using NAudio and use it later?

I'm playing the same sound files (randomly choosing between them) 5 times a second, and I'm always loading into memory, so the program uses a lot of memory. How could I load the sound file into memory, and start it from there? I'm using NAudio. Current code:
var sound = "sounds/test.mp3";
using (var audioFile = new AudioFileReader(sound))
using (var outputDevice = new WaveOutEvent())
{
outputDevice.Init(audioFile);
outputDevice.Play();
while (outputDevice.PlaybackState == PlaybackState.Playing)
{
Thread.Sleep(1000);
}
threadStop();
}
If you remove the using blocks then audioFile and outputDevice will not be disposed. You can then retain them in memory, and the same references will be used each time you play the audio.
With using blocks, you are repeatedly instantiating NAudio objects whose memory may not be deallocated immediately.
var sound = "sounds/test.mp3";
var audioFile = new AudioFileReader(sound);
var outputDevice = new WaveOutEvent();
outputDevice.Init(audioFile);
outputDevice.Play();
while (outputDevice.PlaybackState == PlaybackState.Playing)
{
Thread.Sleep(1000);
}
threadStop();
I fixed the whole issue by using the code found in this article.
It uses the MixingSampleProvider. I load the sounds into a custom class called: CachedSound. And then I play them using another class called: AudioPlaybackEngine. Which handles the mixer, and I use the CachedSoundSampleProvider class to read the cached sound.
The code looks like this:
class AudioPlaybackEngine : IDisposable
{
private readonly IWavePlayer outputDevice;
private readonly MixingSampleProvider mixer;
public AudioPlaybackEngine(int sampleRate = 44100, int channelCount = 2)
{
outputDevice = new WaveOutEvent();
mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channelCount));
mixer.ReadFully = true;
outputDevice.Init(mixer);
outputDevice.Play();
}
public void PlaySound(string fileName)
{
var input = new AudioFileReader(fileName);
AddMixerInput(new AutoDisposeFileReader(input));
}
private ISampleProvider ConvertToRightChannelCount(ISampleProvider input)
{
if (input.WaveFormat.Channels == mixer.WaveFormat.Channels)
{
return input;
}
if (input.WaveFormat.Channels == 1 && mixer.WaveFormat.Channels == 2)
{
return new MonoToStereoSampleProvider(input);
}
throw new NotImplementedException("Not yet implemented this channel count conversion");
}
public void PlaySound(CachedSound sound)
{
AddMixerInput(new CachedSoundSampleProvider(sound));
}
private void AddMixerInput(ISampleProvider input)
{
mixer.AddMixerInput(ConvertToRightChannelCount(input));
}
public void Dispose()
{
outputDevice.Dispose();
}
public static readonly AudioPlaybackEngine Instance = new AudioPlaybackEngine(44100, 2);
}
class CachedSound
{
public float[] AudioData { get; private set; }
public WaveFormat WaveFormat { get; private set; }
public CachedSound(string audioFileName)
{
using (var audioFileReader = new AudioFileReader(audioFileName))
{
// TODO: could add resampling in here if required
WaveFormat = audioFileReader.WaveFormat;
var wholeFile = new List<float>((int)(audioFileReader.Length / 4));
var readBuffer= new float[audioFileReader.WaveFormat.SampleRate * audioFileReader.WaveFormat.Channels];
int samplesRead;
while((samplesRead = audioFileReader.Read(readBuffer,0,readBuffer.Length)) > 0)
{
wholeFile.AddRange(readBuffer.Take(samplesRead));
}
AudioData = wholeFile.ToArray();
}
}
}
class CachedSoundSampleProvider : ISampleProvider
{
private readonly CachedSound cachedSound;
private long position;
public CachedSoundSampleProvider(CachedSound cachedSound)
{
this.cachedSound = cachedSound;
}
public int Read(float[] buffer, int offset, int count)
{
var availableSamples = cachedSound.AudioData.Length - position;
var samplesToCopy = Math.Min(availableSamples, count);
Array.Copy(cachedSound.AudioData, position, buffer, offset, samplesToCopy);
position += samplesToCopy;
return (int)samplesToCopy;
}
public WaveFormat WaveFormat { get { return cachedSound.WaveFormat; } }
}
// This class automatically disposes the file reader that it contains.
class AutoDisposeFileReader : ISampleProvider
{
private readonly AudioFileReader reader;
private bool isDisposed;
public AutoDisposeFileReader(AudioFileReader reader)
{
this.reader = reader;
this.WaveFormat = reader.WaveFormat;
}
public int Read(float[] buffer, int offset, int count)
{
if (isDisposed)
return 0;
int read = reader.Read(buffer, offset, count);
if (read == 0)
{
reader.Dispose();
isDisposed = true;
}
return read;
}
public WaveFormat WaveFormat { get; private set; }
}

Can't get a com port (over usb) to work without opening and closing it once

I'm building an application where DMX output is generated. The application than gives this data via an USB interface to the hardware. Since DMX uses the RS485 protocol, almost any RS485 USB-to-Serial converter can be used. I want my application to be extendable by user written plugins, which inherit from my base class which than handles the communication between the application and the interface.
So here is my problem:
I can't communicate with the interface when the interface is plugged in after the app was started. Im using WqlEventQuery's and the ManagementEventWatcher classes to detect usb event's (plugin & out) and load & unload my classes accordingly. While debugging I noticed that the driver class indeed see's the device, but can't read from it (eg the serial number). Do I need to reset something before the port get's usable?
status get's set to FT_INVALID_HANDLE after FT_Open which means that a communication is never accomplished.
The FTD2xx class is working as it should be except when the interface is plugged in after starting the app.
EDIT
I got it working. However to make the serial usable in my application I have to open, close and dispose it once from the C# SerialPort class once. Why is that?
var ports = SerialPort.GetPortNames();
SerialPort port = new SerialPort(ports[0]);
port.Open();
port.Close();
port.Dispose();
-> Following code works
Code:
Bases driver class
public abstract class Driver
{
public abstract string FullName { get; set; }
public virtual string Description { get; set; }
public abstract string[] HardwareIDs { get; set; }
public virtual int FrameRate { get; set; }
public Driver(int framerate)
{
this.FrameRate = framerate;
}
public abstract void Start();
public abstract void Stop();
public abstract void SetDMXValue(int channel, byte value);
}
FTD2xx driver class
public class FTD2XX : Driver
{
public override string FullName
{
get { return "FTD2XX Usb-Interface"; }
set {}
}
public override string Description
{
get { return "Driver for the FTD2xx usb-to-dmx interface"; }
set {}
}
public override string[] HardwareIDs
{
get { return new string[] { #"USB\VID_0403&PID_6001" }; }
set {}
}
public byte[] buffer { get; private set; }
public uint handle;
public bool done = false;
public int bytesWritten = 0;
public FT_STATUS status;
public const byte BITS_8 = 8;
public const byte STOP_BITS_2 = 2;
public const byte PARITY_NONE = 0;
public const UInt16 FLOW_NONE = 0;
public const byte PURGE_RX = 1;
public const byte PURGE_TX = 2;
Thread workThread;
// DLL IMPORT'S LEFT OUT FOR SIZE //
public FTD2XX(int frameRate) : base(frameRate)
{
this.buffer = new byte[255];
}
public override void Start()
{
handle = 0;
status = FT_Open(0, ref handle);
workThread = new Thread(new ThreadStart(writeData));
workThread.Start();
}
public override void SetDMXValue(int channel, byte value)
{
if (buffer != null)
{
buffer[channel] = value;
}
}
public void writeData()
{
while (!done)
{
InitOpenDMX();
FT_SetBreakOn(handle);
FT_SetBreakOff(handle);
bytesWritten = write(handle, buffer, buffer.Length);
Thread.Sleep(FrameRate);
}
}
public int write(uint handle, byte[] data, int length)
{
IntPtr ptr = Marshal.AllocHGlobal((int)length);
Marshal.Copy(data, 0, ptr, (int)length);
uint bytesWritten = 0;
status = FT_Write(handle, ptr, (uint)length, ref bytesWritten);
return (int)bytesWritten;
}
public void InitOpenDMX()
{
status = FT_ResetDevice(handle);
status = FT_SetDivisor(handle, (char)12); // set baud rate
status = FT_SetDataCharacteristics(handle, BITS_8, STOP_BITS_2, PARITY_NONE);
status = FT_SetFlowControl(handle, (char)FLOW_NONE, 0, 0);
status = FT_ClrRts(handle);
status = FT_Purge(handle, PURGE_TX);
status = FT_Purge(handle, PURGE_RX);
}
public override void Stop()
{
this.done = true;
this.workThread.Abort();
this.workThread = null;
status = FT_ResetDevice(handle);
status = FT_Close(handle);
}
/// <summary>
/// Enumaration containing the varios return status for the DLL functions.
/// </summary>
public enum FT_STATUS
{
FT_OK = 0,
FT_INVALID_HANDLE,
FT_DEVICE_NOT_FOUND,
FT_DEVICE_NOT_OPENED,
FT_IO_ERROR,
FT_INSUFFICIENT_RESOURCES,
FT_INVALID_PARAMETER,
FT_INVALID_BAUD_RATE,
FT_DEVICE_NOT_OPENED_FOR_ERASE,
FT_DEVICE_NOT_OPENED_FOR_WRITE,
FT_FAILED_TO_WRITE_DEVICE,
FT_EEPROM_READ_FAILED,
FT_EEPROM_WRITE_FAILED,
FT_EEPROM_ERASE_FAILED,
FT_EEPROM_NOT_PRESENT,
FT_EEPROM_NOT_PROGRAMMED,
FT_INVALID_ARGS,
FT_OTHER_ERROR
};
}
}
USB event handler
private void DeviceInsertedEvent(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
foreach(Driver driver in this.drivers)
{
if (driver.HardwareIDs.Any(hID => (instance.Properties["DeviceID"].Value as string).StartsWith(hID)))
{
driver.Start();
if (this.OnDriverLoaded != null)
this.OnDriverLoaded(this, new PluginLoadUnloadEventArgs(driver));
break;
}
}
}
Unplug is basiclly the same, but .Stop() and OnDriverUnload() used
Detecting any compatible usb devices after startup (Code works as desired if this is the case)
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(#"Select * From Win32_USBHub"))
collection = searcher.Get();
foreach (var device in collection)
{
foreach (Driver driver in this.drivers)
{
if (driver.HardwareIDs.Any(hID => (device.GetPropertyValue("DeviceID") as string).StartsWith(hID)))
{
driver.Start();
if (this.OnDriverLoaded != null)
this.OnDriverLoaded(this, new PluginLoadUnloadEventArgs(driver));
goto End;
}
}
}
End:
return;

Low level audio player with C# playback stutter - fix?

I am working on a small project (just for fun) that allows me to display the frequencies played back in my current audio buffer.
The plan is:
The thread that is executing the WaveOutPlayer.ThreadProc() method keeps playing and refilling two audio buffers. While the first buffer is playing, the second buffer gets refilled and vice versa. To be sure that one buffer does not start playing before the other one stopped playback, I use the WaitOne() method. After the buffer's audio data has beed played back by the WaveOut device, the buffer's OnCompleted() method is called. This method cancels the waiting.
The playback itself starts, but it is very choppy.
Could anyone tell me why this happens?
The Visual Studio project is located at http://www.pour-toujours.net/lowlevelaudio.zip
(currently, only 16-bit, Stereo, 44,1kHz uncompressed wav is supported.)
Here goes some of my code that seems to be relevant:
using System;
using System.Threading;
using System.Runtime.InteropServices;
using System.Collections;
using System.Linq;
namespace LowLevelAudio
{
internal class WaveOutHelper
{
public static void Try(int err)
{
if (err != WaveNative.MMSYSERR_NOERROR)
throw new Exception(err.ToString());
}
}
public delegate void BufferFillEventHandler(IntPtr data, int size);
public class WaveOutBuffer : IDisposable
{
private AutoResetEvent m_PlayEvent = new AutoResetEvent(false);
private IntPtr m_WaveOut;
private int m_buffersize;
private static byte[] m_samples;
private static double[] m_fftsamples;
private WaveNative.WaveHdr m_Header;
private byte[] m_HeaderData;
private GCHandle m_HeaderHandle;
private GCHandle m_HeaderDataHandle;
private WaveFormat m_waveformat;
private double[] m_fftOccurances;
private double[] m_fftHertzlist;
private bool m_Playing;
public int ID
{
get; set;
}
internal static void WaveOutProc(IntPtr hdrvr, int uMsg, int dwUser, ref WaveNative.WaveHdr wavhdr, int dwParam2)
{
if (uMsg == WaveNative.MM_WOM_DONE)
{
try
{
GCHandle h = (GCHandle)wavhdr.dwUser;
WaveOutBuffer buf = (WaveOutBuffer)h.Target;
buf.OnPlayCompleted();
}
catch(Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
}
}
}
public WaveOutBuffer(IntPtr waveOutHandle, int size, WaveFormat format)
{
m_WaveOut = waveOutHandle;
m_waveformat = format;
m_HeaderHandle = GCHandle.Alloc(m_Header, GCHandleType.Pinned);
m_Header.dwUser = (IntPtr)GCHandle.Alloc(this);
m_HeaderData = new byte[size];
m_HeaderDataHandle = GCHandle.Alloc(m_HeaderData, GCHandleType.Pinned);
m_Header.lpData = m_HeaderDataHandle.AddrOfPinnedObject();
m_Header.dwBufferLength = size;
m_buffersize = size;
m_samples = new byte[m_buffersize];
WaveOutHelper.Try(WaveNative.waveOutPrepareHeader(m_WaveOut, ref m_Header, Marshal.SizeOf(m_Header)));
}
~WaveOutBuffer()
{
Dispose();
}
public int Size
{
get { return m_Header.dwBufferLength; }
}
public IntPtr Data
{
get { return m_Header.lpData; }
}
public void Dispose()
{
if (m_Header.lpData != IntPtr.Zero)
{
WaveNative.waveOutUnprepareHeader(m_WaveOut, ref m_Header, Marshal.SizeOf(m_Header));
m_HeaderHandle.Free();
m_Header.lpData = IntPtr.Zero;
}
m_PlayEvent.Close();
if (m_HeaderDataHandle.IsAllocated)
m_HeaderDataHandle.Free();
}
public bool Play()
{
lock(this) // works, but has to be fine tuned... (to do)
{
m_PlayEvent.Reset();
m_Playing = WaveNative.waveOutWrite(m_WaveOut, ref m_Header, Marshal.SizeOf(m_Header)) == WaveNative.MMSYSERR_NOERROR;
if (!m_Playing)
throw new Exception("test exception");
return m_Playing;
}
}
public void WaitForMe()
{
Console.WriteLine(this.ID + " WaitFor()");
if (m_Playing)
{
m_Playing = m_PlayEvent.WaitOne();
}
else
{
m_Playing = false;
}
}
public void OnPlayCompleted()
{
Console.WriteLine(this.ID + " OnCompleted()");
m_PlayEvent.Set();
m_Playing = false;
}
}
public class WaveOutPlayer : IDisposable
{
private IntPtr m_WaveOut;
private WaveOutBuffer[] m_bufferlist;
private Thread m_Thread;
private BufferFillEventHandler m_FillProc;
private bool m_Finished;
private byte m_zero;
private int m_buffercount = 0;
private int m_buffersize = 0;
private WaveFormat m_waveformat;
private WaveNative.WaveDelegate m_BufferProc = new WaveNative.WaveDelegate(WaveOutBuffer.WaveOutProc);
public static int DeviceCount
{
get { return WaveNative.waveOutGetNumDevs(); }
}
public WaveOutPlayer(int device, WaveFormat format, int bufferSize, BufferFillEventHandler fillProc)
{
m_zero = format.wBitsPerSample == 8 ? (byte)128 : (byte)0;
m_FillProc = fillProc;
m_buffercount = 2;
m_buffersize = bufferSize;
m_waveformat = format;
WaveOutHelper.Try(WaveNative.waveOutOpen(out m_WaveOut, device, format, m_BufferProc, 0, WaveNative.CALLBACK_FUNCTION));
AllocateBuffers(bufferSize, m_buffercount, format);
m_Thread = new Thread(new ThreadStart(ThreadProc));
m_Thread.Start();
}
~WaveOutPlayer()
{
Dispose();
}
public void Dispose()
{
if (m_Thread != null)
{
try
{
m_Finished = true;
if (m_WaveOut != IntPtr.Zero)
WaveNative.waveOutReset(m_WaveOut);
m_Thread.Join();
m_FillProc = null;
FreeBuffers();
if (m_WaveOut != IntPtr.Zero)
WaveNative.waveOutClose(m_WaveOut);
}
finally
{
m_Thread = null;
m_WaveOut = IntPtr.Zero;
}
}
}
private void ThreadProc()
{
WaveOutBuffer b0 = m_bufferlist[0];
WaveOutBuffer b1 = m_bufferlist[1];
MainForm form = Program.getUI();
bool s = true;
m_FillProc(b0.Data, b0.Size);
while (!m_Finished)
{
if (s)
{
Console.WriteLine("-------------------------");
Console.WriteLine("Playing b0, filling b1");
b0.Play();
m_FillProc(b1.Data, b1.Size);
form.paintEqualizer(b0);
Console.WriteLine("callng waitFor on b0");
b0.WaitForMe();
}
else
{
Console.WriteLine("-------------------------");
Console.WriteLine("Playing b1, filling b0");
b1.Play();
m_FillProc(b0.Data, b0.Size);
form.paintEqualizer(b1);
Console.WriteLine("callng waitFor on b1");
b1.WaitForMe();
}
s = !s;
}
}
private void AllocateBuffers(int bufferSize, int bufferCount, WaveFormat format)
{
FreeBuffers();
m_bufferlist = new WaveOutBuffer[m_buffercount];
if (bufferCount > 0)
{
for (int i = 0; i < m_buffercount; i++)
{
m_bufferlist[i] = new WaveOutBuffer(m_WaveOut, bufferSize, format);
m_bufferlist[i].ID = i;
}
}
}
private void FreeBuffers()
{
if (m_bufferlist != null)
{
foreach (WaveOutBuffer currentBuffer in m_bufferlist)
{
if (currentBuffer != null)
{
currentBuffer.Dispose();
}
}
}
}
}
}

Error on opening MemoryMappedFile in another process, C# compact framework

I have created a MemoryMappedFile using C#.Net Compact Framework on WinCE7.
When I am trying to open the same MemorymappedFile in another process, I am getting a null file handle.
Here is the code I am using.
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 Overloads
public 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 Overloads
public 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 Constructors
internal 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();
}
}
}
Application for creating the memory mapped file and writing to it, this works OK.
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();
}
}
}
This works good, i can see the file created and I can write to it.
Application for opening the memory mapped file and reading from it, it does not work.
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
Actually I am trying to share the memory mapped file between driver(written in C++) and user app(written in C#).

Categories

Resources