I have an application written in C# which plays little .wav files. It uses the SoundPlayer class in the System.Media namepace to play the sounds, using a thread that calls the SoundPlayer.PlaySync method to play the .wav file. It's all wrapped up in a class that looks like this:
public class SoundController {
private object soundLocker = new object();
protected Thread SoundThread { get; set; }
protected string NextSound { get; set; }
protected AutoResetEvent PlayASoundPlag { get; set; }
protected Dictionary<string, SoundPlayer> Sounds { get; set; }
protected bool Stopping { get; set; }
public string SoundPlaying { get; private set; }
public SoundController() {
PendingCount = 0;
PlayASoundFlag = new AutoResetEvent( false );
Sounds = new Dictionary<string, SoundPlayer>();
soundLocker = new object();
Stopping = false;
SoundThread = new Thread( new ThreadStart( SoundPlayer ) ) { Name = "SoundThread", IsBackground = true };
SoundThread.Start();
}
private void SoundPlayer() {
do {
PlayASoundFlag.WaitOne();
bool soundWasPlayed = false;
while ( !Stopping && NextSound != null ) {
lock ( soundLocker ) {
SoundPlaying = NextSound;
NextSound = null;
}
Sounds[ SoundPlaying ].PlaySync();
lock ( soundLocker ) {
SoundPlaying = null;
soundWasPlayed = true;
}
}
} while ( !Stopping );
}
public bool HasSound( string key ) {
return Sounds.ContainsKey( key );
}
public void PlayAlarmSound( string key, bool stopCurrentSound ) {
if ( !Sounds.ContainsKey( key ) )
throw new ArgumentException( "Sound unknown", "key" );
lock ( soundLocker ) {
NextSound = key;
if ( SoundPlaying != null && stopCurrentSound )
Sounds[ SoundPlaying ].Stop();
PlayASoundFlag.Set();
}
}
}
When my program calls the PlaySound method, and a sound is currently playing, the Stop method is called, but the sound that's playing doesn't actually stop. I've placed trace points on the call to Stop and a line I added after it just so I could see when the call was made and when it returned, while listening with headphones. It's obvious that the sound plays all the way through to the end.
How do I get the sounds to stop playing reliably?
Unfortunately this is a messy process, but this works:
class Program
{
static void Main(string[] args)
{
SoundPlayerEx player = new SoundPlayerEx(#"c:\temp\sorry_dave.wav");
player.SoundFinished += player_SoundFinished;
Console.WriteLine("Press any key to play the sound");
Console.ReadKey(true);
player.PlayAsync();
Console.WriteLine("Press a key to stop the sound.");
Console.ReadKey(true);
player.Stop();
Console.WriteLine("Press any key to continue");
}
static void player_SoundFinished(object sender, EventArgs e)
{
Console.WriteLine("The sound finished playing");
}
}
public static class SoundInfo
{
[DllImport("winmm.dll")]
private static extern uint mciSendString(
string command,
StringBuilder returnValue,
int returnLength,
IntPtr winHandle);
public static int GetSoundLength(string fileName)
{
StringBuilder lengthBuf = new StringBuilder(32);
mciSendString(string.Format("open \"{0}\" type waveaudio alias wave", fileName), null, 0, IntPtr.Zero);
mciSendString("status wave length", lengthBuf, lengthBuf.Capacity, IntPtr.Zero);
mciSendString("close wave", null, 0, IntPtr.Zero);
int length = 0;
int.TryParse(lengthBuf.ToString(), out length);
return length;
}
}
public class SoundPlayerEx : SoundPlayer
{
public bool Finished { get; private set; }
private Task _playTask;
private CancellationTokenSource _tokenSource = new CancellationTokenSource();
private CancellationToken _ct;
private string _fileName;
private bool _playingAsync = false;
public event EventHandler SoundFinished;
public SoundPlayerEx(string soundLocation)
: base(soundLocation)
{
_fileName = soundLocation;
_ct = _tokenSource.Token;
}
public void PlayAsync()
{
Finished = false;
_playingAsync = true;
Task.Run(() =>
{
try
{
double lenMs = SoundInfo.GetSoundLength(_fileName);
DateTime stopAt = DateTime.Now.AddMilliseconds(lenMs);
this.Play();
while (DateTime.Now < stopAt)
{
_ct.ThrowIfCancellationRequested();
//The delay helps reduce processor usage while "spinning"
Task.Delay(10).Wait();
}
}
catch (OperationCanceledException)
{
base.Stop();
}
finally
{
OnSoundFinished();
}
}, _ct);
}
public new void Stop()
{
if (_playingAsync)
_tokenSource.Cancel();
else
base.Stop(); //To stop the SoundPlayer Wave file
}
protected virtual void OnSoundFinished()
{
Finished = true;
_playingAsync = false;
EventHandler handler = SoundFinished;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
So why doesn't it work "normally"? Its a well known problem. The SoundPlayer is a "fire and forget" piece of code, and if you don't cancel it on the same thread that you started it on, it will not do anything. A lot of people complain about it and as I'm sure you've seen there are very few solutions out side of using raw wave_out calls or moving to DirectX (or with WPF, using the MediaPlayer control).
This SoundPlayerEx class has a couple properties that let you know when the sound is finished or to cancel playing a sound that you started asynchronously. There is no need to create a new thread to work on, making it a lot easier to use.
Feel free to expand on the code, it was a quick and dirty solution to your problem. The two classes you need are the SoundInfo class and the SoundPlayerEx class, the rest of the code above is a demo (replace the wav file with one of your own).
Note this is not a universal solution as it relies on the winmm.dll, so this will not port over to Mono (not sure if Mono has a SoundPlayer class or not). Also since its a dirty solution, you won't get the Finished event or property if you don't use the PlayAsync call.
Related
I would monitor data received on a Serial port with my pc and a Arduino.
On the arduino, the sketch send thorugt the USB the string "aabb" evry 300ms.
With pc I want listen, and in real time print the string in a control (Textbox). To do that, I create a new thread which listen in a Loop what arrives in Serial port, and when it happens it write by a Invoke the string in textbox. The procedures works if I deploy in the form's class but if I use a external class it doesn't. To explain better the matter, I paste the code of the class
class SerialPortManager
{
public SerialPort Serial = new SerialPort();
private Thread thr;
private string Log;
public TextBox textLog;
public string LastString;
public bool thrIsAlive;
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[IODescriptionAttribute("ControlInvokeRequiredDescr")]
public bool InvokeRequired { get; private set; }
//DISPOSE
public void Dispose()
{
this.Dispose();
}
//SET Textobox LOG
public void SetLogTxtB (TextBox txt)
{
textLog = txt;
}
//PORTE DISPONIBILI
public string[] Available_Ports()
{
return SerialPort.GetPortNames();
}
//COSTRUTTORI
public SerialPortManager(string portname, int baudrate,bool InitializeConn)
{
Serial.BaudRate = baudrate;
Serial.PortName = portname;
if (InitializeConn == true) Serial.Open();
}
public SerialPortManager()
{
}
//SETTA I PARAMETRI E INIZIALIZZA LA CONNESSIONE
public void SetConnectionParam(string portname, int baudrate, bool initializeConn)
{
Serial.Close();
Serial.Dispose();
Serial = new SerialPort();
Serial.BaudRate = baudrate;
Serial.PortName = portname;
if (initializeConn == true) Serial.Open();
}
//ASYNC LISTENER
public void AsyncListener()
{
thrIsAlive = true;
thr = new Thread(ThreadReader);
thr.Start();
}
//PROCEDURA PER APPEND
public void AppendTextBox(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(AppendTextBox), new object[] { value });
return;
}
textLog.Text += value;
}
private void Invoke(Action<string> action, params object[] v)
{
throw new NotImplementedException();
}
void ThreadReader()
{
while (thrIsAlive)
{
string temp = Serial.ReadLine();
LastString = temp;
Log += LastString + "\n";
AppendTextBox(LastString + "\n");
}
}
}
In the form I write three rows
SerialPortManager PortMan = new Driver_Arduin.SerialPortManager("COM3", 9600,true);
PortMan.SetLogTxtB(textBox1);
PortMan.AsyncListener();
If I try to run program it returns the error " cross-thread operation not allowed". Now, while I posting this ask, I decide to do a last try and change the method AppendTextBox to :
public void AppendTextBox(string value)
{
if (textLog.InvokeRequired)
{
try
{
textLog.Invoke(new Action<string>(AppendTextBox), new object[] { value });
return;
}
catch (ObjectDisposedException)
{
thrIsAlive = false;
}
}
textLog.Text += value;
}
And It Finally works. Now ascertained the power of Stackoverflow that solved the problem before posting, I would know why my code works. Thank you
In SerialPortManager you must use delegate instead windows control.
class SerialPortManager
{
public SerialPort Serial = new SerialPort();
private Thread thr;
private string Log;
//public TextBox textLog;
public Action<string> textLog;
.....
Crete in you form simply method:
public void SetTextBoxText(string value)
{
if (textBox1.InvokeRequired)
{
try
{
textBox1.Invoke(new Action<string>(AppendTextBox), new object[] { value });
return;
}
catch (ObjectDisposedException)
{
thrIsAlive = false;
}
}
textBox1.Text += value;
}
Set delegate to PortMan:
SerialPortManager PortMan = new Driver_Arduin.SerialPortManager("COM3", 9600,true);
PortMan.SetLogTxtB=new Action<string>(SetTextBoxText);
PortMan.AsyncListener();
If need output log to TextBox of PortMan call textLog delegate.
void ThreadReader()
{
while (thrIsAlive)
{
string temp = Serial.ReadLine();
LastString = temp;
Log += LastString + "\n";
//AppendTextBox(LastString + "\n");
textLog(LastString + "\n");
}
}
Apart from that your Invoke method in SerialPortManager should throw NotImplementedException the problem is that you define your own InvokeRequired/Invoke.
You need to use these methods provided by a WinForms control such that it knows whether your code is running inside the thread (UI thread) that created the control and how it can change context to this thread.
Actually it seems you may use your SerialPortManager but make use of InvokeRequired/Invoke of textLog like you're already doing in AppendTextBox.
BTW, if (initializeConn == true) is rather useless - if (initializeConn) is sufficient.
I trying to allow people to write to NFC tags using my app, so that my app gets launched with a custom parameter. I want to be able to reprogram NFC tags which already have data on them.
I am using the following code but the problem is, that WP always recognizes the action which is already on the NFC tag and interrupts because it wants to launch the NFC tag action which was written anytime before.
How can I tell the OS to stop triggering the action of the tag so that I can immediately rewrite it?
public enum NfcHelperState
{
Initializing,
Waiting,
Ready,
Writing,
Finished,
Error,
NoDeviceFound
}
public class NfcHelper
{
private NfcHelperState _state = NfcHelperState.Initializing;
public NfcHelperState State
{
get { return _state; }
}
private ProximityDevice _nfcDevice;
private long _subscriptionId;
public NfcHelper()
{
Init();
}
public void Init()
{
UpdateState();
_nfcDevice = ProximityDevice.GetDefault();
if (_nfcDevice == null)
{
UpdateState(NfcHelperState.NoDeviceFound);
return;
}
UpdateState(NfcHelperState.Waiting);
}
private void UpdateState(NfcHelperState? state = null)
{
if (state.HasValue)
{
_state = state.Value;
}
if (OnStatusMessageChanged != null)
{
OnStatusMessageChanged(this, _state);
}
}
public void WriteToTag()
{
UpdateState(NfcHelperState.Ready);
_subscriptionId = _nfcDevice.SubscribeForMessage("WriteableTag", WriteableTagDetected);
}
private void WriteableTagDetected(ProximityDevice sender, ProximityMessage message)
{
UpdateState(NfcHelperState.Writing);
try
{
var str = "action=my_custom_action";
str += "\tWindowsPhone\t";
str += CurrentApp.AppId;
_nfcDevice.PublishBinaryMessage("LaunchApp:WriteTag", GetBufferFromString(str),
WriteToTagComplete);
}
catch (Exception e)
{
UpdateState(NfcHelperState.Error);
StopWaitingForTag();
}
}
private void WriteToTagComplete(ProximityDevice sender, long messageId)
{
sender.StopPublishingMessage(messageId);
UpdateState(NfcHelperState.Finished);
StopWaitingForTag();
}
private void StopWaitingForTag()
{
_nfcDevice.StopSubscribingForMessage(_subscriptionId);
}
private static IBuffer GetBufferFromString(string str)
{
using (var dw = new DataWriter())
{
dw.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;
dw.WriteString(str);
return dw.DetachBuffer();
}
}
public delegate void NfcStatusMessageChangedHandler(object myObject, NfcHelperState newState);
public event NfcStatusMessageChangedHandler OnStatusMessageChanged;
}
WriteToTag is called when a button in my app is tapped and the app waits for a writable tag. If a writable tag is recognized, WriteableTagDetected gets called and immediately starts the writing process. However, this is interrupted by the WP dialog which asks whether to perform the NFC action or not. After writing, WriteToTagComplete should be called, where StopWaitingForTag gets called and ends the write process.
I hope you guys can help me :)
Turns out I thought the wrong way. I didn't need to wait for a tag to arrive in order to rewrite it. In fact, there's no need to do _nfcDevice.SubscribeForMessage("WriteableTag", WriteableTagDetected); before writing. Just start using PublishBinaryMessage and it will write to the tag once it arrives at the device.
My final code looks like the following:
public enum NfcHelperState
{
Initializing,
Ready,
WaitingForWriting,
FinishedWriting,
ErrorWriting,
NoDeviceFound
}
public class NfcHelper
{
private NfcHelperState _state = NfcHelperState.Initializing;
public NfcHelperState State
{
get { return _state; }
}
private ProximityDevice _nfcDevice;
private long? _writingMessageId;
public NfcHelper()
{
Init();
}
public void Init()
{
UpdateState();
_nfcDevice = ProximityDevice.GetDefault();
if (_nfcDevice == null)
{
UpdateState(NfcHelperState.NoDeviceFound);
return;
}
UpdateState(NfcHelperState.Ready);
}
private void UpdateState(NfcHelperState? state = null)
{
if (state.HasValue)
{
_state = state.Value;
}
if (OnStatusMessageChanged != null)
{
OnStatusMessageChanged(this, _state);
}
}
public void WriteToTag()
{
StopWritingMessage();
UpdateState(NfcHelperState.WaitingForWriting);
try
{
var str = new StringBuilder();
str.Append("action=my_custom_action");
str.Append("\tWindowsPhone\t{");
str.Append(CurrentApp.AppId);
str.Append("}");
_writingMessageId = _nfcDevice.PublishBinaryMessage("LaunchApp:WriteTag", GetBufferFromString(str.ToString()),
WriteToTagComplete);
}
catch
{
UpdateState(NfcHelperState.ErrorWriting);
StopWritingMessage();
}
}
private void WriteToTagComplete(ProximityDevice sender, long messageId)
{
UpdateState(NfcHelperState.FinishedWriting);
StopWritingMessage();
}
private void StopWritingMessage()
{
if (_writingMessageId.HasValue)
{
_nfcDevice.StopPublishingMessage(_writingMessageId.Value);
_writingMessageId = null;
}
}
private static IBuffer GetBufferFromString(string str)
{
using (var dw = new DataWriter())
{
dw.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;
dw.WriteString(str);
return dw.DetachBuffer();
}
}
public delegate void NfcStatusMessageChangedHandler(object myObject, NfcHelperState newState);
public event NfcStatusMessageChangedHandler OnStatusMessageChanged;
}
I have a simple pattern to run code only once. It's mostly used to Update something on the UI, while it may change very often in the Background.
private bool _updating;
private void UpdateSomething()
{
if (!_updating)
{
_updating = true;
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
_updating = false;
DoSomething();
}), DispatcherPriority.Background);
}
}
I would prefer to put the boilerplate code inside a simple method:
public static void RunOnce(Action action, ref bool guard)
{
if (!guard)
{
guard = true;
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
guard = false;
action();
}), DispatcherPriority.Background);
}
}
und call it like this:
void UpdateSomething()
{
RunOnce(DoSomething, ref _updating);
}
However, this does not work as you cannot have ref parameters inside anonymous methods.
Is there any workaround, e.g. to pin the ref parameter and free it when the method was executed?
You could do something like this:
public static void RunOnce(Action action, ref RunOnceToken token)
{
if (token == null || token.IsCompleted)
{
token = new RunOnceToken(
Application.Current.Dispatcher.BeginInvoke(
action,
DispatcherPriority.Background));
}
}
public sealed class RunOnceToken : IDisposable
{
private DispatcherOperation _operation;
public RunOnceToken(DispatcherOperation operation)
{
if (operation != null &&
operation.Status != DispatcherOperationStatus.Completed &&
operation.Status != DispatcherOperationStatus.Aborted)
{
_operation = operation;
_operation.Completed += OnCompletedOrAborted;
_operation.Aborted += OnCompletedOrAborted;
}
}
private void OnCompletedOrAborted(object sender, EventArgs e)
{
this.Dispose();
}
public bool IsCompleted
{
get { return _operation == null; }
}
public void Dispose()
{
var operation = _operation;
if (operation == null)
return;
_operation = null;
operation.Completed -= OnCompletedOrAborted;
operation.Aborted -= OnCompletedOrAborted;
}
}
Your example usage would change to:
private RunOnceToken _updateToken;
private void UpdateSomething()
{
RunOnce(DoSomething, ref _updateToken);
}
It doesn't really matter if you never clear your copy of the token, because the wrapped DispatcherOperation gets cleared out upon completion to avoid leaking action or any values it captures.
In case it wasn't obvious, none of this is concurrency-safe; I assume everything above is only accessed from the UI thread.
One useful enhancement might be to add an optional DispatcherPriority argument to RunOnce such that you can control the priority level used to schedule action (perhaps canceling an already-scheduled operation if it was scheduled at a lower priority).
I was no aware about DispatcherOperation existence, however seen Mike Strobel answer I wrote following code. I'm not 100% sure about it but it seems to work without to much boilerplate.
public static class DispatcherExtensions {
public static int clearInterval = 10_000;
private static long time => DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
private static long lastClearTime = time;
private static Dictionary<int, DispatcherOperation> currOperations = new Dictionary<int, DispatcherOperation>();
private static object sync = new object();
public static void invokeLastAsync(this Dispatcher d, Action a, DispatcherPriority p = DispatcherPriority.Background, [CallerFilePath]object key1 = null, [CallerLineNumber]object key2 = null) {
lock (sync) {
DispatcherOperation dop;
var k = key1.GetHashCode() ^ key2.GetHashCode();
if (currOperations.ContainsKey(k)) {
dop = currOperations[k];
currOperations.Remove(k);
dop.Abort();
}
dop = d.BeginInvoke(a, p);
clearOperations(false);
currOperations.Add(k, dop);
}
}
public static void clearOperations(bool force = true) {
var ct = time;
if (!force && ct - lastClearTime < clearInterval) return;
var nd = new Dictionary<int, DispatcherOperation>();
foreach (var ao in currOperations) {
var s = ao.Value.Status;
if (s == DispatcherOperationStatus.Completed
|| s == DispatcherOperationStatus.Aborted)
nd.Add(ao.Key, ao.Value);
}
currOperations = nd;
lastClearTime = ct;
}
}
Basically extension method take file path and line number as a key to store DispacherOperation instance in a dictionary, and If the key already have an operation, its aborted and replaced with new operation. Periodically, the dictionary is cleared, from completed/aborted actions that are no longer invoked.
The usage is very simple:
private int initCount = 0;
private int invokeCount = 0;
private void updateSomething() {
initCount++;
view.Dispatcher.invokeLastAsync(() => {
Console.WriteLine($#"invoked {++invokeCount}/{initCount}");
});
}
I haven't run to any issue with this so far. Maybe someone else could see some weak spot.
I am developing a multithreaded application in C#, and I have now come to point where I have realised that my threads sometimes throw errors when I stop them via the .Abort(); .Join(); methods.
My current code for starting and stopping the thread is as follows:
public void StartLogging()
{
if (poller != null && poller.IsAlive)
{
poller.Abort();
poller.Join();
}
poller = new Thread(new ThreadStart(PollUSBDevice));
poller.IsBackground = true;
poller.Name = reference.VendorId.ToString() + ":" + reference.ProductId.ToString();
poller.Start();
IsLogging = true;
}
public void StopLogging()
{
if (poller != null && poller.IsAlive)
{
poller.Abort();
poller.Join();
IsLogging = false;
}
}
private void PollUSBDevice()
{
...Removed code - executes within milliseconds and I am not worried about stopping here.
ErrorCode ec = ErrorCode.None;
### THIS LOOPS FOR EVER OR UNTIL I CALL .Abort() ###
while (ec == ErrorCode.None && MyUsbDevice.IsOpen)
{
if (poller.ThreadState == System.Threading.ThreadState.AbortRequested)
{
reader.Abort();
reader.Dispose();
break;
}
else
{
byte[] readBuffer = new byte[8];
int bytesRead;
ec = reader.Read(readBuffer, 100, out bytesRead);
Application.Current.Dispatcher.BeginInvoke(
new OneArgDelegate(HandleData),
new object[] { readBuffer });
}
}
}
catch (Exception ex)
{
Do stuff....
}
finally
{
Close devices that are running in above while statement
}
}
I have tried other methods post here on Stackoverflow, however I just can't get my head around them (I'm newish to multithreading). Preferably, there would just be a bool switch on my parent object reference that I could check. IE:
public class Reference
{
public static bool gracefulStopRequested = false;
}
public void PollUSBDevice
{
while (ec == ErrorCode.None && !reference.gracefulStopRequested)
{
....
}
}
Can anyone point me to a good resource or give me a hint as to what search terms I should be searching for, or if you are in a really giving mood, possibly do a mockup of how you would handle this problem?
I would go for something like:
class Program
{
static void Main(string[] args)
{
Thread poller = new Thread(new ThreadStart(PollUSBDevice));
poller.Start();
Console.ReadLine();
StopPoller();
Console.WriteLine("Stopped");
Console.ReadLine();
}
public static void StopPoller()
{
_PollerStopRequested = true;
}
private static bool _PollerStopRequested = false;
private static void PollUSBDevice()
{
while (true && !_PollerStopRequested)
{
Console.WriteLine("running");
Thread.Sleep(500);
}
}
}
However this is just simulating a build in feature of C# BackgroundWorker, so you could also have a look at: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx
I need to request values using functions in a DLL provided by the manufacturer of my particular piece of hardware (a weather station). I'm new to C#, and the concepts of delegates/events are tough to wrap my head around. Nonetheless, I've managed to pull the functions from the DLL and verify that data makes it through. My issue is with polling the instrument periodically with a Timer. In Initialize(), an object is instantiated, but the event isn't handled leaving the object null. I'm out of ideas, and would like some advice!
public class HardwareData : EventArgs
{
public float OutsideTemp { get; set; }
public int OutsideHum { get; set; }
public float WindSpeed { get; set; }
public int WindDirection { get; set; }
}
public class Hardware : IDisposable
{
private static Hardware v;
private System.Timers.Timer hardwareTimer;
private int counter = 0;
private static readonly object padlock = new object();
public static Hardware Instance
{
get
{
lock (padlock)
{
if (v == null)
v = new Hardware();
return v;
}
}
}
public void Initialize()
{
try
{
hardwareTimer = new System.Timers.Timer(500);
hardwareTimer.Elapsed += new ElapsedEventHandler(hardwareTimer_Elapsed);
HardwareVue.OpenCommPort_V(3, 19200); //COM port and baud rate are verified.
hardwareTimer.Start();
}
catch (Exception ex)
{
throw new InvalidOperationException("Unable to initialize.", ex);
}
}
public HardwareData LastHardware { get; set; }
void hardwareTimer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
counter += 1;
Console.WriteLine(counter);
HardwareVue.LoadCurrentHardwareData_V();
HardwareData v = new HardwareData()
{
OutsideTemp = HardwareVue.GetOutsideTemp_V(),
OutsideHum = HardwareVue.GetOutsideHumidity_V(),
WindSpeed = HardwareVue.GetWindSpeed_V(),
WindDirection = HardwareVue.GetWindDir_V()
};
LastHardware = v;
}
catch (Exception) { }
}
public void Dispose()
{
HardwareVue.CloseCommPort_V();
hardwareTimer.Stop();
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
Hardware test = new Hardware();
try
{
if (test != null)
{
test.Initialize();
test.Dispose();
Assert.AreEqual(0, test.LastHardware.OutsideHum);
}
}
catch (NullReferenceException ex)
{
Console.WriteLine("Object is null.");
}
// Console.WriteLine(test.LastHardware.OutsideHum);
}
}
When working with timers, you need to enable the timer and make sure the events are rasied:
hardwareTimer.Enabled = true;
hardwareTimer.CanRaiseEvents = true;
For Reference: Timers on MSDN
Edit
In addition to the other comments to both the OP's question and this answer, the issue with the LastHardware being null is due to the property never being instantiated before the timer initially fires. To resolve this, you should instantiate the LastHardware property in the default constructor (or in the Initialize method):
public Hardware()
{
LastHardware = new HardwareData();
}
Of course, you'd want to set some default values upon instantiation.