I am developing a small SMS server in C# and want to make an asynchronous listener that processes requests sent to an SMS object (username, message, etc.). I want to create the asynchronous listener as a class library. Here is the code:
using System;
using System.Net;
using System.Threading;
namespace AListener
{
public class CAListener
{
public CAListener()
{
Thread listener = new Thread(new ParameterizedThreadStart(RequestListener));
listener.Start(new string[] { "http://*:11600/" });
}
private ManualResetEvent stopListener = new ManualResetEvent(false);
private object lockListener = new object();
private bool newRequest = false;
public void StopListener()
{
stopListener.Set();
}
public void RequestListener(object parameter)
{
string[] prefixes = parameter as string[];
HttpListener listener = new HttpListener();
foreach (string prefix in prefixes)
{
listener.Prefixes.Add(prefix);
}
listener.Start();
IAsyncResult result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
while (!stopListener.WaitOne(10))
{
lock (lockListener)
{
if (newRequest)
{
result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
newRequest = false;
}
}
result.AsyncWaitHandle.WaitOne(10);
}
listener.Close();
}
public void ListenerCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
// Call EndGetContext to complete the asynchronous operation.
HttpListenerContext context = listener.EndGetContext(result);
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Construct a response.
string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
// You must close the output stream.
output.Close();
lock (lockListener)
{
newRequest = true;
}
}
}
}
I have a UI form where I obtain AListener object and everything here is OK.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AListener;
namespace ZNSMS
{
public partial class ZNSMSServer : Form
{
public ZNSMSServer()
{
InitializeComponent();
}
CAListener ca = null;
private void button1_Click(object sender, EventArgs e)
{
ca.StopListener();
}
private void ZNSMSServer_Load(object sender, EventArgs e)
{
ca = new CAListener();
}
}
}
But when I press the Button (I just want to stop the listener) - for some reason listener.Stop() kills my main UI Form ZNSMSServer. When I comment listener.Stop() it's working, but I think that's not the proper way and I cannot understand why calling listener.Stop() can impact (in this case Close?) the calling object - in this case ZNSMSServer.
If anyone has dealt with this kind of issue before, I would really appreciate it.
Thanks in advance.
Related
I need help with creating a windows service using Threading and asynchronous HttpWebRequest calls. I have created a few C# windows services before but never using threading. Also, I seem to be getting hung up with the async calls using HttpWebRequest. I have googled this as well as looking on this site. I could not find anything that helped. This is mainly because I could not seem to get what was presented in other questions to work in my specific example.
Please keep in mind that I may be overlapping things based on my lack of knowledge in this specific area as well as through trying to figure it out.
The main flow of this is to get a list of urls during onStart. Typically this list would be retrieved from a _facade.GetUrls call. Then, at each time interval call scanSites. A request is made to each url and then I save the results to the database in _facade.SaveUrlResponse.
My problems is it seems as if I am caught in an endless loop when I debug it. I am not exactly sure how/where to do this. Thanks in advance.
Here is what I have:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.ServiceProcess;
using System.Threading;
using URLValidation.BusinessManager.Facade;
using URLValidation.BusinessManager.Model;
namespace URLValidation
{
partial class URLValidation : ServiceBase
{
#region " class variables "
private System.Timers.Timer _timer;
private List<UrlModel> _url_List = null;
private Facade _facade;
private Thread _t;
private int _x;
#endregion
public URLValidation()
{
_facade = new Facade();
InitializeComponent();
}
protected override void OnStart(string[] args)
{
try
{
_url_List = new List<UrlModel>
{
new UrlModel(address: "http://www.google.com", addressID: 1),
new UrlModel(address: "http://www.microsoft.com", addressID: 2),
new UrlModel(address: "http://www.stackoverflow.com", addressID: 3)
};
resetTimer();
GC.KeepAlive(_timer);
}
catch (Exception ex)
{
throw ex;
}
}
private void resetTimer()
{
try
{
_timer = new System.Timers.Timer();
_timer.Interval = 10000;//1800000; //30 minutes
_timer.Start();
_timer.Enabled = true;
_timer.Elapsed += scanSites;
}
catch (Exception ex)
{
throw ex;
}
}
private void scanSites(object sender, System.Timers.ElapsedEventArgs e)
{
_timer.Stop();
_x = 0;
_t = new Thread(new ThreadStart(scanSites));
_t.IsBackground = true;
_t.Start();
}
private void scanSites()
{
try
{
foreach (UrlModel url in _url_List)
{
_x += 1;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url.Address);
request.Method = "HEAD";
RequestModel requestModel = new RequestModel(request, url);
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(saveUrlResponse), requestModel);
ThreadPool.RegisterWaitForSingleObject
(
result.AsyncWaitHandle,
new WaitOrTimerCallback(ScanTimeoutCallback),
requestModel,
(30 * 1000), // 30 second timeout
true
);
}
}
catch (Exception ex)
{
throw ex;
}
}
private void saveUrlResponse(IAsyncResult result)
{
//grab the custom state object
RequestModel requestModel = (RequestModel)result.AsyncState;
HttpWebRequest request = (HttpWebRequest)requestModel.Request;
//get the Response
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
// process the response...
ResponseModel responseModel = new ResponseModel(request, response, requestModel.UrlModel.AddressID);
_facade.SaveUrlResponse(responseModel);
}
private void ScanTimeoutCallback(object requestModel, bool timedOut)
{
if (timedOut)
{
RequestModel reqState = (RequestModel)requestModel;
if (reqState != null)
reqState.Request.Abort();
}
if (_x == _url_List.Count)
{
resetTimer();
}
}
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
}
}
}
Okay, I think I am getting somewhere. I have moved my code to a console app. I am able to get the results saved to the database by using either GetResponse (sync) and BeginGetResponse (async). From what I can tell I believe this is a good solution. Can somebody verify this and let me know if you foresee any problems once this is moved to a Windows Service. Here is the new code
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
namespace ConsoleApplication2
{
static class Program
{
private static List<UrlModel> _url_List = null;
private static Object _acctLock = new object();
private static Facade _facade = new Facade();
static void Main(string[] args)
{
_url_List = new List<UrlModel>
{
new UrlModel(address: "http://www.microsoft.com", addressID: 1),
new UrlModel(address: "http://www.google.com", addressID: 2),
new UrlModel(address: "http://www.stackoverflow.com", addressID: 3)
};
lockThreadAndGetUrlStatus(_url_List);
Console.ReadLine();
}
static void lockThreadAndGetUrlStatus(List<UrlModel> _url_List)
{
Thread[] threads;
try
{
threads = new Thread[_url_List.Count];
Thread.CurrentThread.Name = "main";
int i = 0;
foreach (UrlModel url in _url_List)
{
//Thread t = new Thread(() => scanSites(url));
Thread t = new Thread(() => scanSitesWithAsync(url));
t.Name = i.ToString();
threads[i] = t;
i += 1;
}
for (i = 0; i < _url_List.Count; i++)
{
Console.WriteLine("Thread {0} Alive : {1}", threads[i].Name, threads[i].IsAlive);
threads[i].Start();
Console.WriteLine("Thread {0} Alive : {1}", threads[i].Name, threads[i].IsAlive);
}
Console.WriteLine("Current Priority : {0}", Thread.CurrentThread.Priority);
Console.WriteLine("Thread {0} Ending", Thread.CurrentThread.Name);
}
catch (Exception ex)
{
throw ex;
}
}
static void scanSitesWithAsync(UrlModel url)
{
try
{
lock (_acctLock)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url.Address);
request.Method = "HEAD";
RequestModel requestModel = new RequestModel(request, url);
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(saveUrlResponseWithAsync), requestModel);
ThreadPool.RegisterWaitForSingleObject
(
result.AsyncWaitHandle,
new WaitOrTimerCallback(scanTimeoutCallback),
requestModel, 30000, true
);
}
}
catch (Exception ex)
{
throw ex;
}
}
static void saveUrlResponseWithAsync(IAsyncResult result)
{
try
{
RequestModel requestModel = (RequestModel)result.AsyncState;
HttpWebRequest request = (HttpWebRequest)requestModel.Request;
//get the Response
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
// process the response...
ResponseModel responseModel = new ResponseModel(requestModel.Request, response, requestModel.UrlModel.AddressID);
_facade.SaveUrlResponse(responseModel);
Console.WriteLine(response.StatusCode);
}
catch (Exception ex)
{
throw ex;
}
}
static void scanTimeoutCallback(object requestModel, bool timedOut)
{
try
{
if (timedOut)
{
RequestModel reqState = (RequestModel)requestModel;
if (reqState != null)
reqState.Request.Abort();
}
}
catch (Exception ex)
{
throw ex;
}
}
}
}
looks like you are trying to issue 3 async requests at the same time. By default, the HTTP/1.1 protocol only specifies 2 connections
So i am building an app using xamarin.forms which needs to play and control audio. I have currently got my audio file playing on both android and iOS BUT I can't get other controls to work such as .Pause() and .Stop()
Playing audio works in both examples.
I thank you in advance!
Here is my code for each platform
ANDROID:
using System;
using Xamarin.Forms;
using AudioPlayEx.Droid;
using AudioToolbox;
using Android.Media;
using Android.Content.Res;
using Android.OS;
using Android;
[assembly: Dependency(typeof(AudioService))]
namespace AudioPlayEx.Droid
{
public class AudioService : IAudio
{
public AudioService()
{
}
MediaPlayer player;
public void PlayAudioFile(string fileName)
{
var player = new MediaPlayer();
var fd = Android.App.Application.Context.Assets.OpenFd(fileName);
player.Prepared += (s, e) =>
{
player.Start();
};
player.SetDataSource(fd.FileDescriptor, fd.StartOffset, fd.Length);
player.Prepare();
}
protected MediaPlayer playme;
public void PauseAudioFile(String fileName)
{
var player = new MediaPlayer();
var fd = Android.App.Application.Context.Assets.OpenFd(fileName);
player.Prepared += (s, e) =>
{
player.Pause();
};
player.SetDataSource(fd.FileDescriptor, fd.StartOffset, fd.Length);
player.Prepare();
}
}
}
iOS:
using System;
using Xamarin.Forms;
using AudioPlayEx;
using AudioPlayEx.iOS;
using System.IO;
using Foundation;
using AVFoundation;
[assembly: Dependency(typeof(AudioService))]
namespace AudioPlayEx.iOS
{
public class AudioService : IAudio
{
public AudioService()
{
}
public void PlayAudioFile(string fileName)
{
string sFilePath = NSBundle.MainBundle.PathForResource(Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName));
var url = NSUrl.FromString(sFilePath);
var _player = AVAudioPlayer.FromUrl(url);
_player.FinishedPlaying += (object sender, AVStatusEventArgs e) => {
_player = null;
};
_player.Play();
}
public void PauseAudioFile(string fileName)
{
string sFilePath = NSBundle.MainBundle.PathForResource(Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName));
var url = NSUrl.FromString(sFilePath);
var _player = AVAudioPlayer.FromUrl(url);
_player.Pause();
}
}
}
I think you need to save your instance of AVAudioPlayer to a variable and call pause on that object. Right now you're creating a new AVAudioPlayer and calling pause on it, which is doing nothing. I've added iOS code to show what I mean. Android should be similar.
iOS:
public class AudioService : IAudio
{
AVAudioPlayer _player;
public void PlayAudioFile(string fileName)
{
string sFilePath = NSBundle.MainBundle.PathForResource(Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName));
var url = NSUrl.FromString(sFilePath);
_player = AVAudioPlayer.FromUrl(url);
_player.FinishedPlaying += (object sender, AVStatusEventArgs e) => {
_player = null;
};
_player.Play();
}
public void PauseAudioFile(string fileName)
{
_player.Pause();
}
}
That topic is old, but I did it that way:
MediaPlayer player;
public void PlayAudioFile(string fileName, string ONOF)
{
if (ONOF == "OFF")
{
player.Stop();
}
if (ONOF == "ON")
{
player = new MediaPlayer();
var fd = global::Android.App.Application.Context.Assets.OpenFd(fileName);
player.Prepared += (s, e) =>
{
player.Start();
};
player.SetDataSource(fd.FileDescriptor, fd.StartOffset, fd.Length);
player.Prepare();
}
}
I am working with an application that will call OData Service. I tried the Simple.OData.Client but I can't get it working..
Here is the code that I try
var client = new ODataClient("http://packages.nuget.org/v1/FeedService.svc/");
var packages = await client.FindEntriesAsync("Packages?$filter=Title eq 'Simple.OData.Client'");
foreach (var package in packages)
{
Console.WriteLine(package["Title"]);
}
I get this error
Error 1 The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
using System;
using Simple.OData.Client;
namespace ODataClient
{
class Program
{
static void Main(string[] args)
{
new Manager().GetData();
Console.ReadLine();
}
}
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ODataClient
{
public class Manager
{
private readonly Simple.OData.Client.ODataClient client;
public Manager()
{
client = new Simple.OData.Client.ODataClient("http://packages.nuget.org/v1/FeedService.svc/");
}
public void GetData()
{
try
{
IEnumerable<IDictionary<string, object>> response = GetPackages().Result;
foreach (var package in response)
{
Console.WriteLine(package["Title"]);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private async Task<IEnumerable<IDictionary<string, object>>> GetPackages()
{
var packages = await client.FindEntriesAsync("Packages?$filter=Title eq 'Simple.OData.Client'");
return packages;
}
}
}
This is even simpler. We need SimpleQuery method, since we cannot add the async keyword to the Main or any entry point methods.
static async void SimpleQuery()
{
var client = new ODataClient("http://blahb...lah.svc/");
try
{
var packages = await client.FindEntriesAsync("Products");
foreach (var package in packages)
{
Console.WriteLine(package);
}
} catch (Exception e)
{
Console.WriteLine("Simple Query " + e);
}
}
static void Main(string[] args)
{
Console.WriteLine("Press Enter when the job is completed");
SimpleQuery();
Console.ReadLine();
}
I found some sample code posted at
https://groups.google.com/group/microsoft.public.dotnet.languages.csharp/browse_thread/thread/4d45e9ea5471cba4/4519371a77ed4a74?hl=en&pli=1
for self installing a Windows Service. I am in C# on fx 4.0. Trying
to figure out where I went off the rails...
My questions:
I created a Win Service project. In program.cs / main() I pretty much
copied the code example. It appears most everything is working
except launching a console window if I am in DEBUG mode (passing in -
c of course). For some reason the console window never opens.
The other challenge I had was the call to StartUp() / ShutDown() in
the console portion would not compile. I ended up have to initialize
my service object and then call it.
I am getting the following error when the Console.ReadKey() method is called:
Cannot read keys when either
application does not have a console or
when console input has been redirected
from a file. Try Console.Read.
My code and stuff:
An image of my project structure:
NOTE: I was duplicating the startup sequence in the TestHarness when
in DEBUG mode. If/when I get this working I will be dropping that
from the solution. The Library project is where the majority of my
code lives.
PROGRAM.CS
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.ComponentModel;
using System.Configuration.Install;
using System.Collections;
using RivWorks.FeedHandler.Service;
namespace RivWorks.FeedHandler
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static int Main(string[] args)
{
bool install = false, uninstall = false, console = false, rethrow = false;
try
{
foreach (string arg in args)
{
switch (arg)
{
case "-i":
case "-install":
install = true; break;
case "-u":
case "-uninstall":
uninstall = true; break;
case "-c":
case "-console":
console = true; break;
default:
Console.Error.WriteLine("Argument not expected: " + arg);
break;
}
}
if (uninstall)
{
Install(true, args);
}
if (install)
{
Install(false, args);
}
if (console)
{
Console.WriteLine("Starting...");
FeedListener fl = new FeedListener();
fl.StartUp();
Console.WriteLine("System running; press any key to stop");
Console.ReadKey(true);
fl.ShutDown();
Console.WriteLine("System stopped");
}
else if (!(install || uninstall))
{
rethrow = true; // so that windows sees error...
ServiceBase[] services = { new Service.FeedListener() };
ServiceBase.Run(services);
rethrow = false;
}
return 0;
}
catch (Exception ex)
{
if (rethrow) throw;
Console.Error.WriteLine(ex.Message);
return -1;
}
}
static void Install(bool undo, string[] args)
{
try
{
Console.WriteLine(undo ? "uninstalling" : "installing");
using (AssemblyInstaller inst = new AssemblyInstaller(typeof(Program).Assembly, args))
{
IDictionary state = new Hashtable();
inst.UseNewContext = true;
try
{
if (undo)
{
inst.Uninstall(state);
}
else
{
inst.Install(state);
inst.Commit(state);
}
}
catch
{
try
{
inst.Rollback(state);
}
catch { }
throw;
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
}
}
[RunInstaller(true)]
public sealed class MyServiceInstallerProcess : ServiceProcessInstaller
{
public MyServiceInstallerProcess()
{
this.Account = ServiceAccount.NetworkService;
}
}
[RunInstaller(true)]
public sealed class MyServiceInstaller : ServiceInstaller
{
public MyServiceInstaller()
{
this.Description = "Provides a service to listen for, then import, feed files from various sources.";
this.DisplayName = "RIVWorks Feed Handler (.NET 4.0)";
this.ServiceName = "FeedListener";
this.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
}
}
}
FEEDLISTENER.CS
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.IO;
using sysIO = System.IO;
using RivWorks.FeedHandler;
using System.Collections;
using RivWorks.FeedHandler.Library;
using System.Threading;
namespace RivWorks.FeedHandler.Service
{
public partial class FeedListener : ServiceBase
{
#region Declarations
static private List<string> _keys = new List<string>();
static private System.Threading.Timer _clock = null;
static private FileSystemWatcher _watcher;
static private RivWorks.FeedHandler.Library.QueueHandler _qHandler = null;
static private bool _isDequeueing = false;
#endregion
#region Constructor
public FeedListener()
{
InitializeComponent();
}
#endregion
#region Internal Methods
internal void StartUp() {...}
internal void ShutDown() {...}
#endregion
#region Start/Stop
protected override void OnStart(string[] args)
{
StartUp();
}
protected override void OnStop()
{
ShutDown();
}
#endregion
#region Event Handlers
static void qHandler_QueuesGrew() {...}
static void qHandler_QueuesShrunk() {...}
static void qHandler_QueuesChanged() {...}
static void fileCreatedOrChanged(object sender, sysIO.FileSystemEventArgs e) {...}
#endregion
#region Private Methods
private static void Tick(object state) {...}
private static void WriteToEventLog(Exception ex, EventLogEntryType eventLogEntryType) {...}
private static void WriteToEventLog(string message, EventLogEntryType eventLogEntryType) {...}
#endregion
}
}
And I found my answer! My project properties were set to Windows App instead of Console App. DOH! (Project Properties > Application Tab > Output type:)
Also.. instead of using -console arg, you can identify its console by using Environment.UserInteractive, which will be false when running as service, but true when running the EXE directly.
You can also have a look at this: http://www.thedavejay.com/2012/04/self-installing-c-windows-service-safe.html
It allows you to debug as a console application, and install the same app as a windows service without having to change the project type.
So I'm a newbie to TDD, and I successfully created a nice little sample app using the MVP pattern. The major problem to my current solution is that its blocking the UI thread, So I was trying to setup the Presenter to use the SynchronizationContext.Current, but when I run my tests the SynchronizationContext.Current is null.
Presenter Before Threading
public class FtpPresenter : IFtpPresenter
{
...
void _view_GetFilesClicked(object sender, EventArgs e)
{
_view.StatusMessage = Messages.Loading;
try
{
var settings = new FtpAuthenticationSettings()
{
Site = _view.FtpSite,
Username = _view.FtpUsername,
Password = _view.FtpPassword
};
var files = _ftpService.GetFiles(settings);
_view.FilesDataSource = files;
_view.StatusMessage = Messages.Done;
}
catch (Exception ex)
{
_view.StatusMessage = ex.Message;
}
}
...
}
Test Before Threading
[TestMethod]
public void Can_Get_Files()
{
var view = new FakeFtpView();
var presenter = new FtpPresenter(view, new FakeFtpService(), new FakeFileValidator());
view.GetFiles();
Assert.AreEqual(Messages.Done, view.StatusMessage);
}
Now after I added a SynchronizationContext Threading to the Presenter I tried to set a AutoResetEvent on my Fake View for the StatusMessage, but when I run the test the SynchronizationContext.Current is null. I realize that the threading model I'm using in my new Presenter isn't perfect, but is this the right technique for Testing Multithreading? Why is my SynchronizationContext.Current null? What should I do instead?
Presenter After Threading
public class FtpPresenter : IFtpPresenter
{
...
void _view_GetFilesClicked(object sender, EventArgs e)
{
_view.StatusMessage = Messages.Loading;
try
{
var settings = new FtpAuthenticationSettings()
{
Site = _view.FtpSite,
Username = _view.FtpUsername,
Password = _view.FtpPassword
};
// Wrap the GetFiles in a ThreadStart
var syncContext = SynchronizationContext.Current;
new Thread(new ThreadStart(delegate
{
var files = _ftpService.GetFiles(settings);
syncContext.Send(delegate
{
_view.FilesDataSource = files;
_view.StatusMessage = Messages.Done;
}, null);
})).Start();
}
catch (Exception ex)
{
_view.StatusMessage = ex.Message;
}
}
...
}
Test after threading
[TestMethod]
public void Can_Get_Files()
{
var view = new FakeFtpView();
var presenter = new FtpPresenter(view, new FakeFtpService(), new FakeFileValidator());
view.GetFiles();
view.GetFilesWait.WaitOne();
Assert.AreEqual(Messages.Done, view.StatusMessage);
}
Fake View
public class FakeFtpView : IFtpView
{
...
public AutoResetEvent GetFilesWait = new AutoResetEvent(false);
public event EventHandler GetFilesClicked = delegate { };
public void GetFiles()
{
GetFilesClicked(this, EventArgs.Empty);
}
...
private List<string> _statusHistory = new List<string>();
public List<string> StatusMessageHistory
{
get { return _statusHistory; }
}
public string StatusMessage
{
get
{
return _statusHistory.LastOrDefault();
}
set
{
_statusHistory.Add(value);
if (value != Messages.Loading)
GetFilesWait.Set();
}
}
...
}
I've run into similar problems with ASP.NET MVC where it is the HttpContext that is missing. One thing you can do is provide an alternate constructor that allows you to inject a mock SynchronizationContext or expose a public setter that does the same thing. If you can't change the SynchronizationContext internally, then make a property that you set to the SynchronizationContext.Current in the default constructor and use that property throughout your code. In your alternate constructor, you can assign the mock context to the property -- or you can assign to it directly if you give it a public setter.
public class FtpPresenter : IFtpPresenter
{
public SynchronizationContext CurrentContext { get; set; }
public FtpPresenter() : this(null) { }
public FtpPresenter( SynchronizationContext context )
{
this.CurrentContext = context ?? SynchronizationContext.Current;
}
void _view_GetFilesClicked(object sender, EventArgs e)
{
....
new Thread(new ThreadStart(delegate
{
var files = _ftpService.GetFiles(settings);
this.CurrentContext.Send(delegate
{
_view.FilesDataSource = files;
_view.StatusMessage = Messages.Done;
}, null);
})).Start();
...
}
One other observation that I would make is that I would probably have your presenter depend on an interface to the Thread class rather than on Thread directly. I don't think that your unit tests should be creating new threads but rather interacting with a mock class that just ensures that the proper methods to create threads get called. You could inject that dependency as well.
If the SynchronizationContext.Current doesn't exist when the constructor is called, you may need to move the assignment logic to Current into the getter and do lazy load.
You have to much app-logic in your presenter. I would hide contexts and threads inside a concrete model and test the functionality alone.