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();
}
}
}
}
}
}
Related
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; }
}
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();
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#).
How do I check the connection between PPC and a web service on the server? I work on FW3.5 C#
I can't remember offhand if this is available in .Net CF or not:
System.Net.NetworkInformation.NetworkChange.NetworkAvailabilityChanged
Another option is to just attempt to use the web service, and handle exceptions. I usually follow this pattern, is there a reason it doesn't work for your case?
Edit:
Connection Monitor class:
public static class ConnectionMonitor
{
public static event EventHandler IsConnectedChanged;
private static SystemState _connectionState = null,
//_cellState = null,
_gprsState = null;
private static int _connectionCount = 0;
//_cellCount = 0;
private static bool _gprs = false;
private static bool _isConnected = true;
private static string _phoneCarrier = SystemState.PhoneOperatorName;
private const int POWER_FLAGS = 0x00000001; // default
private const string AXIM_WIFI_ADAPTER = "{98C5250D-C29A-4985-AE5F-AFE5367E5006}\\TIACXWLN1";
public static bool IsPhone
{
get { return !string.IsNullOrEmpty(_phoneCarrier); }
}
public static bool IsGPRSConnected
{
get { return _gprs; }
}
public static bool IsConnected
{
get { return _isConnected; }
set
{
if (_isConnected != value)
{
_isConnected = value;
if (IsConnectedChanged != null)
{
IsConnectedChanged(null, EventArgs.Empty);
}
if (!_isConnected && !IsPhone)
{
WifiOn();
}
}
}
}
private static void WifiOn()
{
Win32.CEDEVICE_POWER_STATE state = new Win32.CEDEVICE_POWER_STATE();
if (Win32.GetDevicePower(AXIM_WIFI_ADAPTER, POWER_FLAGS, ref state) == 0)
{
if (state != Win32.CEDEVICE_POWER_STATE.D0)
{
Win32.DevicePowerNotify(AXIM_WIFI_ADAPTER, Win32.CEDEVICE_POWER_STATE.D4, POWER_FLAGS);
Win32.SetDevicePower(AXIM_WIFI_ADAPTER, POWER_FLAGS, Win32.CEDEVICE_POWER_STATE.D0);
}
}
}
public static void Init()
{
if (_connectionState == null)
{
_connectionState = new SystemState(SystemProperty.ConnectionsCount);
_connectionCount = SystemState.ConnectionsCount;
_connectionState.Changed += new ChangeEventHandler(_state_Changed);
}
//if (_cellState == null)
//{
// _cellState = new SystemState(SystemProperty.ConnectionsCellularCount);
// _cellCount = SystemState.ConnectionsCellularCount;
// _cellState.Changed += new ChangeEventHandler(_state_Changed);
//}
if (_gprsState == null)
{
_gprsState = new SystemState(SystemProperty.PhoneGprsCoverage);
_gprs = SystemState.PhoneGprsCoverage;
_gprsState.Changed += new ChangeEventHandler(_state_Changed);
}
IsConnected = _connectionCount > 0 || _gprs;
}
private static void _state_Changed(object sender, ChangeEventArgs args)
{
//_cellCount = SystemState.ConnectionsCellularCount;
_connectionCount = SystemState.ConnectionsCount;
_gprs = SystemState.PhoneGprsCoverage;
IsConnected = _connectionCount > 0 || _gprs;
}
public static void Dispose()
{
if (_connectionState != null)
{
_connectionState.Changed -= new ChangeEventHandler(_state_Changed);
_connectionState.Dispose();
_connectionState = null;
}
//if (_cellState != null)
//{
// _cellState.Changed -= new ChangeEventHandler(_state_Changed);
// _cellState.Dispose();
// _cellState = null;
//}
if (_gprsState != null)
{
_gprsState.Changed -= new ChangeEventHandler(_state_Changed);
_gprsState.Dispose();
_gprsState = null;
}
}
}
My Win32 static class had a lot of calls (unrelated to your needs) in it. Here are all the ones I think you need:
public enum CEDEVICE_POWER_STATE : int
{
PwrDeviceUnspecified = -1,
D0 = 0, // Full On: full power, full functionality
D1 = 1, // Low Power On: fully functional at low power/performance
D2 = 2, // Standby: partially powered with automatic wake
D3 = 3, // Sleep: partially powered with device initiated wake
D4 = 4, // Off: unpowered
PwrDeviceMaximum = 5
}
[DllImport("coredll.dll", SetLastError = true)]
public static extern int DevicePowerNotify(string name, CEDEVICE_POWER_STATE state, int flags);
[DllImport("coredll.dll", SetLastError = true)]
public static extern int SetDevicePower(string name, int flags, CEDEVICE_POWER_STATE state);
[DllImport("coredll.dll", SetLastError = true)]
public static extern int GetDevicePower(string name, int flags, ref CEDEVICE_POWER_STATE state);
If I missed any, look them up on pinvoke.net.
I want to hide my mouse cursor after an idle time and it will be showed up when I move the mouse. I tried to use a timer but it didn't work well. Can anybody help me? Please!
If you are using WinForms and will only deploy on Windows machines then it's quite easy to use user32 GetLastInputInfo to handle both mouse and keyboard idling.
public static class User32Interop
{
public static TimeSpan GetLastInput()
{
var plii = new LASTINPUTINFO();
plii.cbSize = (uint)Marshal.SizeOf(plii);
if (GetLastInputInfo(ref plii))
return TimeSpan.FromMilliseconds(Environment.TickCount - plii.dwTime);
else
throw new Win32Exception(Marshal.GetLastWin32Error());
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}
}
And then in your Form
public partial class MyForm : Form
{
Timer activityTimer = new Timer();
TimeSpan activityThreshold = TimeSpan.FromMinutes(2);
bool cursorHidden = false;
public Form1()
{
InitializeComponent();
activityTimer.Tick += activityWorker_Tick;
activityTimer.Interval = 100;
activityTimer.Enabled = true;
}
void activityWorker_Tick(object sender, EventArgs e)
{
bool shouldHide = User32Interop.GetLastInput() > activityThreshold;
if (cursorHidden != shouldHide)
{
if (shouldHide)
Cursor.Hide();
else
Cursor.Show();
cursorHidden = shouldHide;
}
}
}
Here is a contrived example of how to do it. You probably had some missing logic that was overriding the cursor's visibility:
public partial class Form1 : Form
{
public TimeSpan TimeoutToHide { get; private set; }
public DateTime LastMouseMove { get; private set; }
public bool IsHidden { get; private set; }
public Form1()
{
InitializeComponent();
TimeoutToHide = TimeSpan.FromSeconds(5);
this.MouseMove += new MouseEventHandler(Form1_MouseMove);
}
void Form1_MouseMove(object sender, MouseEventArgs e)
{
LastMouseMove = DateTime.Now;
if (IsHidden)
{
Cursor.Show();
IsHidden = false;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
TimeSpan elaped = DateTime.Now - LastMouseMove;
if (elaped >= TimeoutToHide && !IsHidden)
{
Cursor.Hide();
IsHidden = true;
}
}
}
Need to account for Environment.Tickcount being negative:
public static class User32Interop
{
public static TimeSpan GetLastInput()
{
var plii = new LASTINPUTINFO();
plii.cbSize = (uint)Marshal.SizeOf(plii);
if (GetLastInputInfo(ref plii))
{
int idleTime = unchecked(Environment.TickCount - (int)plii.dwTime);
return TimeSpan.FromMilliseconds(idleTime);
}
else
throw new Win32Exception(Marshal.GetLastWin32Error());
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}
}
If you try to hide the cursor from another thread you need to call Invoke(...) on the winform to enter the UI thread. Had cost me some headegg why Cursor.Hide() wasn't working. Also "PInvoke ShowCursor(bool bShow)" has the benefit to return the Show/Hide-Count.
[DllImport("user32.dll")]
static extern int ShowCursor(bool bShow);
private System.Timers.Timer _timerMouseAutoHide;
Point _lastPos = new Point(int.MaxValue);
readonly Stopwatch _stopwatch = new Stopwatch();
private int _cursorVisibilityCount = 0;
private void InitMouseAutoHide()
{
if (_timerMouseAutoHide != null)
return;
_timerMouseAutoHide = new Timer(100){AutoReset = false};
_timerMouseAutoHide.Elapsed += MouseAutoHideHit;
_timerMouseAutoHide.Start();
_stopwatch.Restart();
}
private void MouseAutoHideHit(object pSender, ElapsedEventArgs pE)
{
try
{
if (IsDisposed)
return;
var lCurrentPos = Cursor.Position;
var lCursorHasMoved = _lastPos != lCurrentPos;
_lastPos = lCurrentPos;
if (_cursorVisibilityCount < 0 && lCursorHasMoved)
{
//Cursor is not Visible and Mouse has moved
_cursorVisibilityCount = ShowCursor();
Debug.WriteLine($"Visible {lCurrentPos}, ShowCursor={_cursorVisibilityCount}");
}
else if (_cursorVisibilityCount > -1 && _stopwatch.ElapsedMilliseconds > 3000)
{
//Cursor is Visible and Mouse didn't move fox x ms.
_cursorVisibilityCount = HideCursor();
Debug.WriteLine($"Hidden {lCurrentPos}, ShowCursor={_cursorVisibilityCount}");
}
if (lCursorHasMoved)
_stopwatch.Restart();
}
catch (Exception lEx)
{
GLog.Error(lEx);
}
finally
{
_timerMouseAutoHide?.Start();
}
}
public int ShowCursor()
{
var lCursorVisibilityCount = 0;
Invoke(new Action(() =>
{
do
{
lCursorVisibilityCount = ShowCursor(true);
} while (lCursorVisibilityCount < 0);
}));
return lCursorVisibilityCount;
}
public int HideCursor()
{
var lCursorVisibilityCount = 0;
Invoke(new Action(() =>
{
do
{
lCursorVisibilityCount = ShowCursor(false);
} while (lCursorVisibilityCount > -1);
}));
return lCursorVisibilityCount;
}