Polling using Windows Service - c#

By going through some samples, I referred to Polling Service - C# for my polling.
This is my code.
public partial class Service1 : ServiceBase
{
private readonly PollingService _pollingService = new PollingService();
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
_pollingService.StartPolling();
}
protected override void OnStop()
{
_pollingService.StopPolling();
}
}
public class PollingService
{
private Thread _workerThread;
private AutoResetEvent _finished;
private const int _timeout = 60 * 1000;
string command = "5120000000000000000000000000000";
public void StartPolling()
{
_workerThread = new Thread(Poll);
_finished = new AutoResetEvent(false);
_workerThread.Start();
}
private void Poll()
{
while (!_finished.WaitOne(_timeout))
{
//do the task
using (TcpClient newclient = new TcpClient())
{
IAsyncResult ar = newclient.BeginConnect("192.168.0.151", 4000, null, null);
if (!ar.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(2), false))
{
return;
}
NetworkStream ns = newclient.GetStream();
byte[] outbytes = HexStringToByteArray(command);
ns.Write(outbytes, 0, outbytes.Length);
}
}
}
public void StopPolling()
{
_finished.Set();
_workerThread.Join();
}
public static byte[] HexStringToByteArray(string hexString)
{
if (hexString.Length % 2 > 0)
{
throw new Exception("Invalid command.");
}
byte[] result = new byte[hexString.Length / 2];
try
{
for (int i = 0; i < result.Length; i++)
{
result[i] = Convert.ToByte(hexString.Substring(2 * i, 2), 16);
}
}
catch (Exception)
{
throw;
}
return result;
}
}
I've managed to install. However, when I'm trying to start the service, I'm getting Windows could not start the service on Local Computer. Error 5: Access is denied. I've tried using the solutions given here, Error 5 : Access Denied when starting windows service, however, it is not working.

I found the solution by changing the service properties from This Account to Local System Account with some updated codes.
using (TcpClient newclient = new TcpClient("192.168.0.151", 4000))
{
NetworkStream ns = newclient.GetStream();
byte[] outbytes = Encoding.ASCII.GetBytes(command);
ns.Write(outbytes, 0, outbytes.Length);
ns.Close();
newclient.Close();
}

Related

MPI.NET Send/ImmediateProbe not working when using multiple hosts

I developed an MPI test program where the master node distributes work to the worker nodes.
The worker node uses comm.Send() to request work and the master node checks with comm.ImmediateProbe if any of the worker nodes wants to request some work. If a request is available it is read with comm.Receive and the work is sent to the worker for processing.
When I run my test program with mpiexec.exe on a single host, either localhost or also a remote host everything works as expected, but when I run it on two hosts at the same time
the Send on the remote host blocks and the master nodes ImmediateProbe never receives the message sent from the worker on the remote host.
I run the program with mpiexec.exe -wdir \\DESKTOP-58QONBS\MPITest -hosts 2 DESKTOP-58QONBS 2 LAPTOP-L8F7AN5R 1 MPITest.exe
I'm new to MPI, so maybe I am doing something wrong I just could not figure out yet why the behaviour is like this when using two hosts at the same time.
The full code is below:
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
namespace MPITest
{
public abstract class MPIMasterWorkerBase<TWork, TResult>
where TWork : class
where TResult : class
{
protected abstract void Initialize(bool isMaster);
protected abstract void Main();
protected abstract void ProcessResult(TResult result);
protected abstract TResult ProcessWork(TWork work);
protected abstract TWork GetWork();
private volatile bool terminate = false;
private Thread thread;
private MPI.Intracommunicator comm;
public void Run(string[] args)
{
MPI.Environment.Run(ref args, comm =>
{
this.comm = comm;
if (comm.Size < 2)
{
Console.WriteLine("At least 2 processes are required.");
return;
}
if (comm.Rank == 0)
{
Initialize(isMaster: true);
thread = new Thread(MasterThread);
thread.Start();
Main();
terminate = true;
thread.Join();
}
else
{
Initialize(isMaster: false);
thread = new Thread(WorkerThread);
thread.Start();
thread.Join();
}
});
}
private void MasterThread()
{
Console.WriteLine($"MasterStart {MPI.Environment.ProcessorName}");
var done = new bool[comm.Size];
done[0] = true;
while (!done.All(x => x == true))
{
for (int i = 1; i < comm.Size; i++)
{
if (comm.ImmediateProbe(i, 0) != null)
{
Console.WriteLine($"Receive: {i}");
comm.Receive<int>(i, 0);
var work = GetWork();
if (work != null)
{
comm.Send(1, i, 0);
comm.Send(work, i, 0);
}
else
{
if (terminate)
{
comm.Send(-1, i, 0);
done[i] = true;
}
else
{
comm.Send(0, i, 0);
}
}
}
if (comm.ImmediateProbe(i, 1) != null)
{
var result = comm.Receive<TResult>(i, 1);
ProcessResult(result);
}
}
Thread.Sleep(1000);
}
Console.WriteLine("MasterStop");
}
private void WorkerThread()
{
Console.WriteLine($"WorkerStart: {comm.Rank} {MPI.Environment.ProcessorName}");
while (!terminate)
{
Thread.Sleep(1000);
Console.WriteLine($"Send: {comm.Rank}");
comm.Send(0, 0, 0);
var flag = comm.Receive<int>(0, 0);
if (flag == -1)
break;
else if (flag == 0)
continue;
var work = comm.Receive<TWork>(0, 0);
var result = ProcessWork(work);
comm.Send(result, 0, 1);
}
Console.WriteLine($"WorkerStop: {comm.Rank}");
}
}
[Serializable]
public class WorkItem
{
public int Id { get; set; }
}
public class MPITest : MPIMasterWorkerBase<WorkItem, WorkItem>
{
private ConcurrentQueue<WorkItem> queue = new();
private int id;
protected override void Initialize(bool isMaster)
{
}
protected override void Main()
{
var startTime = DateTime.UtcNow;
while ((DateTime.UtcNow - startTime).TotalSeconds < 10)
{
for (int i = 0; i < 2; i++)
queue.Enqueue(new WorkItem { Id = id++ });
Thread.Sleep(1000);
}
}
protected override WorkItem GetWork()
{
if (queue.TryDequeue(out var result))
return result;
return null;
}
protected override WorkItem ProcessWork(WorkItem work)
{
Console.WriteLine($"Processing Work {work.Id}");
return work;
}
protected override void ProcessResult(WorkItem result)
{
Console.WriteLine($"Process Result {result.Id}");
}
}
class Program
{
static void Main(string[] args)
{
new MPITest().Run(args);
}
}
}
The comm.Send was blocking, but after some minutes of waiting the program started to work.
The issues were caused by the VirtualBox Host-Only Network Adapter that was also installed on the system. Disabling this adapter in the network settings resolved all the issues.

How to fix when trying to add data values to a listbox from another form

I am having an issue while trying to add data to a listbox from my main form, I am trying to add data to that list from a new class in my project, I need that new class to be able to add data to my listbox without errors, I am trying to use a invoke and I am getting the following error (System.InvalidOperationException: 'Invoke or BeginInvoke cannot be called on a control until the window handle has been created.') I have seen that error in other questions in stack overflow but similar to my issue, I will be adding both classes of my code here, one is the main class and the other a second class I created that will need to add data to the listbox. the data is coming from a telnet tcp/ip and port 23 that connection is working fine, the problem is adding that data to my listbox.
Main class calling the functions from my other class
namespace BarcodeReceivingApp
{
//TelnetConnection stopConnection = new TelnetConnection();
public partial class BarcodeReceivingForm : Form
{
//GLOBAL VARIABLES
const string Hostname = "myip";
private const int Port = 23;
public BarcodeReceivingForm()
{
InitializeComponent();
}
private void btn_ConnectT_Click(object sender, EventArgs e)
{
var readData = new TelnetConnection(Hostname, Port);
readData.ServerSocket(Hostname, Port);
}
private void btn_StopConnection_Click(object sender, EventArgs e)
{
//var connection = new TelnetConnection(Hostname, Port);
// connection.CloseConnection();
}
}
}
class that will change the data of my listbox from the main class.
namespace BarcodeReceivingApp
{
public class TelnetConnection
{
public BarcodeReceivingForm BcForm = new BarcodeReceivingForm();
private Thread _readWriteThread;
private TcpClient _client;
private NetworkStream _networkStream;
private string _hostname;
private int _port;
public TelnetConnection(string hostname, int port)
{
this._hostname = hostname;
this._port = port;
}
public void ServerSocket(string ip, int port)
{
try
{
_client = new TcpClient(ip, port);
}
catch (SocketException)
{
MessageBox.Show(#"Failed to connect to server");
return;
}
//Assign networkstream
_networkStream = _client.GetStream();
//start socket read/write thread
_readWriteThread = new Thread(ReadWrite);
_readWriteThread.Start();
}
public void ReadWrite()
{
//Set up connection loop
while (true)
{
var command = "test";
if (command == "STOP1")
break;
//write(command);
var received = Read();
BcForm.lst_BarcodeScan.Invoke(new Action (() => BcForm.lst_BarcodeScan.Items.Add(received)));
}
}
public string Read()
{
byte[] data = new byte[1024];
var received = "";
var size = _networkStream.Read(data, 0, data.Length);
received = Encoding.ASCII.GetString(data, 0, size);
return received;
}
public void CloseConnection()
{
_networkStream.Close();
_client.Close();
}
}
}
the final results like I said is my ReadWrite method when running the loop will add the data to my listbox from my main form class
here the image where I get the error
Image of Error
Write your second class like this
using System;
using System.Threading;
using System.Windows.Forms;
namespace BarcodeReceivingApp {
public class TelnetConnection {
private Thread _readWriteThread;
private TcpClient _client;
private NetworkStream _networkStream;
private string _hostname;
private int _port;
private Form foo;
public TelnetConnection(string hostname, int port)
{
this._hostname = hostname;
this._port = port;
}
public void ServerSocket(string ip, int port,Form f)
{
this.foo = f;
try
{
_client = new TcpClient(ip, port);
}
catch (SocketException)
{
MessageBox.Show(#"Failed to connect to server");
return;
}
_networkStream = _client.GetStream();
_readWriteThread = new Thread(ReadWrite());
_readWriteThread.Start();
}
public void ReadWrite()
{
while (true)
{
var command = "test";
if (command == "STOP1")
break;
//write(command);
var received = Read();
if (foo.lst_BarcodeScan.InvokeRequired)
{
foo.lst_BarcodeScan.Invoke(new MethodInvoker(delegate {foo.lst_BarcodeScan.Items.Add(received);}));
}
}
}
public string Read()
{
byte[] data = new byte[1024];
var received = "";
var size = _networkStream.Read(data, 0, data.Length);
received = Encoding.ASCII.GetString(data, 0, size);
return received;
}
public void CloseConnection()
{
_networkStream.Close();
_client.Close();
}
}
}
Then use it like this from your main class:
private void btn_ConnectT_Click(object sender, EventArgs e)
{
var readData = new TelnetConnection(Hostname, Port);
readData.ServerSocket(Hostname, Port, this);
}

Sockets - Not sending/receiving data

I'm starting a socket program, and am in the process of setting up a Server and two types of Clients (a requester and an arbiter). I'm in the middle of testing the connections, but they aren't quite working. Right now I just have a button for each form: an "Accept" button for the Arbiter and "Request" for the Requester. Each button should cause a popup on the other form, but neither is working. Also, I've noticed that when I close all programs, the Server is still running in my processes. What am I doing wrong?
Below is the Server code:
namespace FPPLNotificationServer
{
class Server
{
static Socket listenerSocket;
static List<ClientData> _clients;
static void Main(string[] args)
{
Console.WriteLine("Starting server on " + Packet.GetIP4Address());
listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clients = new List<ClientData>();
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(Packet.GetIP4Address()), 4242);
listenerSocket.Bind(ip);
Thread listenThread = new Thread(ListenThread);
listenThread.Start();
}
static void ListenThread()
{
for (;;)
{
listenerSocket.Listen(0);
_clients.Add(new ClientData(listenerSocket.Accept()));
}
}
public static void Data_IN(object cSocket)
{
Socket clientSocket = (Socket)cSocket;
byte[] Buffer;
int readBytes;
for (;;)
{
try
{
Buffer = new byte[clientSocket.SendBufferSize];
readBytes = clientSocket.Receive(Buffer);
if(readBytes > 0)
{
Packet packet = new Packet(Buffer);
DataManager(packet);
}
}catch(SocketException ex)
{
Console.WriteLine("Client Disconnected");
}
}
}
public static void DataManager(Packet p)
{
switch (p.packetType)
{
case Packet.PacketType.Notification:
foreach(ClientData c in _clients)
{
c.clientSocket.Send(p.ToBytes());
}
break;
}
}
}
class ClientData
{
public Socket clientSocket;
public Thread clientThread;
public string id;
public ClientData()
{
this.id = Guid.NewGuid().ToString();
clientThread = new Thread(Server.Data_IN);
clientThread.Start(clientSocket);
SendRegistrationPacket();
}
public ClientData(Socket clientSocket)
{
this.clientSocket = clientSocket;
this.id = Guid.NewGuid().ToString();
clientThread = new Thread(Server.Data_IN);
clientThread.Start(clientSocket);
SendRegistrationPacket();
}
public void SendRegistrationPacket()
{
Packet p = new Packet(Packet.PacketType.Registration, "server");
p.Gdata.Add(id);
clientSocket.Send(p.ToBytes());
}
}
}
ServerData
namespace FPPLNotificationServerData
{
[Serializable]
public class Packet
{
public List<String> Gdata;
public int packetInt;
public bool packetBool;
public string senderID;
public PacketType packetType;
public string PlantName, ProductSegment, ProductCustomer;
public int PlantNumber;
public string ProductNumber, ProductAltNumber;
public string ProductDiscription;
public int ProductLine;
public string ProductClass, ProductLocation;
public int ProductMcDFactor;
public Packet(PacketType type, String senderID)
{
Gdata = new List<string>();
this.senderID = senderID;
this.packetType = type;
}
public Packet(byte[] packetBytes)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream(packetBytes);
Packet p = (Packet)bf.Deserialize(ms);
ms.Close();
this.Gdata = p.Gdata;
this.senderID = p.senderID;
this.packetType = p.packetType;
this.packetBool = p.packetBool;
this.packetInt = p.packetInt;
}
public byte[] ToBytes()
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, this);
byte[] bytes = ms.ToArray();
ms.Close();
return bytes;
}
public static string GetIP4Address()
{
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
foreach(IPAddress i in ips)
{
if(i.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
return i.ToString();
}
}
return "127.0.0.1";
}
public enum PacketType
{
Registration,
Chat,
Notification,
Request,
ArbiterDecision,
Accept,
Decline
}
}
}
Request Class:
namespace FPPLRequestClient
{
public partial class frm_Request : Form
{
public static Socket master;
public static string name;
public static string id;
public bool isConnected;
public frm_Request()
{
InitializeComponent();
string IP = "127.0.0.1";
master = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipEP = new IPEndPoint(IPAddress.Parse(IP), 4242);
try
{
master.Connect(ipEP);
isConnected = true;
}
catch (Exception)
{
isConnected = false;
}
string connectionStatus = isConnected ? "Connected" : "Disconnected";
this.lbl_Status.Text = "Status: " + connectionStatus;
Thread t = new Thread(Data_IN);
t.Start();
}
void Data_IN()
{
byte[] Buffer;
int readBytes;
while (isConnected)
{
try
{
Buffer = new byte[master.SendBufferSize];
readBytes = master.Receive(Buffer);
if(readBytes > 0)
{
DataManager(new Packet(Buffer));
}
}catch(SocketException ex)
{
isConnected = false;
this.Dispose();
}
}
}//END DATA IN
void DataManager(Packet p)
{
switch (p.packetType)
{
case Packet.PacketType.Registration:
id = p.Gdata[0];
break;
case Packet.PacketType.Accept:
//MessageBox.Show(p.ProductNumber);
this.lbl_Status.Text = p.ProductNumber + " accepted";
Invalidate();
break;
}
}
private void btn_Request_Click(object sender, EventArgs e)
{
Packet p = new Packet(Packet.PacketType.Request, id);
p.ProductNumber = "123456";
master.Send(p.ToBytes());
}
}
}
Arbiter Class:
namespace FPPLArbiterClient
{
public partial class frm_Arbiter : Form
{
public static Socket master;
public static string name;
public static string id;
public bool isConnected;
public frm_Arbiter()
{
InitializeComponent();
string IP = "127.0.0.1";
master = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipEP = new IPEndPoint(IPAddress.Parse(IP), 4242);
try
{
master.Connect(ipEP);
isConnected = true;
}
catch (Exception)
{
isConnected = false;
}
string connectionStatus = isConnected ? "Connected" : "Disconnected";
this.lbl_Status.Text = "Status: " + connectionStatus;
Thread t = new Thread(Data_IN);
t.Start();
}
void Data_IN()
{
byte[] Buffer;
int readBytes;
while (isConnected)
{
try
{
Buffer = new byte[master.SendBufferSize];
readBytes = master.Receive(Buffer);
if(readBytes > 0)
{
DataManager(new Packet(Buffer));
}
}catch(SocketException ex)
{
isConnected = false;
this.Dispose();
}
}
}//END DATA IN
void DataManager(Packet p)
{
switch (p.packetType)
{
case Packet.PacketType.Registration:
id = p.Gdata[0];
break;
case Packet.PacketType.Request:
MessageBox.Show(p.ProductNumber + " Requested from " + p.senderID);
break;
}
}
private void btn_Accept_Click(object sender, EventArgs e)
{
MessageBox.Show("Sending acceptance of 126456");
Packet p = new Packet(Packet.PacketType.Accept, id);
p.ProductNumber = "123456";
master.Send(p.ToBytes());
}
}
}
This is my first dive into socket programming.
To start with your last question first, Receive will block until data becomes available unless you specify a timeout. Since your threads are foreground threads, this will prevent your application from terminating. See https://msdn.microsoft.com/en-us/library/8s4y8aff(v=vs.110).aspx. Either use a timeout, and/or make your threads background threads causing them to terminate when you close your application's main thread. Set the created thread's IsBackground property to true to achieve this. (Also, in the article above notice the paragraph about Shutdown and the Receive method returning an empty array. This is your hint to gracefully close the connection on your side).
The TCP/IP stack will send data at its own discretion (Nagle's algorithm), meaning you'll occasionally receive a buffer containing several or partial messages. Since you have "silent" error handling in your thread, perhaps your thread terminates prematurely because of a corrupted message? Place everything you receive in a buffer and check the buffer for complete messages in a separate step/thread before passing them on to your message handler.
No clear answers here I'm afraid, but if the check for corrupted messages doesn't help, look at the Socket samples on MSDN. It's probably just a tiny detail you're missing.
you are making a fundamental and common TCP error. TCP is a byte oriented streaming protocol, not message oriented. Your receive code assumes that it receives one Packet when it reads. This is not guaranteed, you might receive 1 byte, 20 bytes, or whatever. You must loop in the receive till you get all of one message. This means you have to know when you have read it all. Either there needs to be a header or some sentinel at the end.

Handling multiple nested asyncs

I am having an issue working with async methods - namely nested async.
I start off a background task:
public sealed class StartupTask : IBackgroundTask
{
public async void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.GetDeferral();
ServerWorkers.WebServer server = new ServerWorkers.WebServer();
await ThreadPool.RunAsync(workItem =>
{
AnotherSync.Get();
server.Start();
});
}
public static async void Get()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(Shared.URL);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Shared.HeaderType));
using (var response = await client.GetAsync(route + "?" + GeneralTags.COMPANY_REF + "=" + ApplicationObject.CompanyRef)) //.Result)
{
if (response.IsSuccessStatusCode)
{
ApplicationObject.PrintData = JsonConvert.DeserializeObject<Model.Print>(response.Content.ReadAsStringAsync().Result);
}
else
{
evError(new Exception(String.Format("{0}: {1}", (int)response.StatusCode, response.ReasonPhrase)), ErrorTags.PRINT_GET);
}
}
}
}
internal class WebServer
{
private const uint BufferSize = 8192;
public void Start()
{
StreamSocketListener listener = new StreamSocketListener();
listener.BindServiceNameAsync("8080");
listener.ConnectionReceived += async (sender, args) =>
{
StringBuilder request = new StringBuilder();
using (IInputStream input = args.Socket.InputStream)
{
byte[] data = new byte[BufferSize];
IBuffer buffer = data.AsBuffer();
uint dataRead = BufferSize;
while (dataRead == BufferSize)
{
await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
dataRead = buffer.Length;
}
}
using (IOutputStream output = args.Socket.OutputStream)
{
using (Stream response = output.AsStreamForWrite())
{
byte[] bodyArray = Encoding.UTF8.GetBytes("<html><body>Hello, World!</body></html>");
var bodyStream = new MemoryStream(bodyArray);
var header = "HTTP/1.1 200 OK\r\n" +
$"Content-Length: {bodyStream.Length}\r\n" +
"Connection: close\r\n\r\n";
byte[] headerArray = Encoding.UTF8.GetBytes(header);
await response.WriteAsync(headerArray, 0, headerArray.Length);
await bodyStream.CopyToAsync(response);
await response.FlushAsync();
}
}
};
}
}
Then my app acts a web server.. It does not exit out.
If I add this so I have:
public async void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.GetDeferral();
ServerWorkers.WebServer server = new ServerWorkers.WebServer();
await ThreadPool.RunAsync(workItem =>
{
AnotherSync.Get();
AnotherSync.Get();
server.Start();
});
}
internal class AnotherSync
{
public static event delError evError;
private const string route = "/api/Print";
static wsPrint.IPrint wsPrint = new wsPrint.PrintClient();
public static void Get()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(Shared.URL);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Shared.HeaderType));
using (var response = client.GetAsync(route + "?" + GeneralTags.COMPANY_REF + "=" + ApplicationObject.CompanyRef).Result)
{
if (response.IsSuccessStatusCode)
{
ApplicationObject.PrintData = JsonConvert.DeserializeObject<Model.Print>(response.Content.ReadAsStringAsync().Result);
}
else
{
evError(new Exception(String.Format("{0}: {1}", (int)response.StatusCode, response.ReasonPhrase)), ErrorTags.PRINT_GET);
}
}
}
}
}
then the application exists out.
I may be mistaken but is it because I am using nested async methods?
This is an example of what a thread should look like,
public sealed class StartupTask : IBackgroundTask
{
//has to be declared here to keep the handle alive and prevent garbage collection
private ServerWorkers.WebServer server = new ServerWorkers.WebServer();
public async void Run(IBackgroundTaskInstance taskInstance)
{
//Start the server running no need for await as its an synchronous
server.Start();
//don't exit until work is compelted
while(server.Running)
{
//give up processor and allow other work to occur while you are waiting
await Task.Yield();
}
}
internal class WebServer
{
private const uint BufferSize = 8192;
private StreamSocketListener listener;
public bool Running { get; private set;}= false;
public void Start()
{
Lock(this){//prevent any other code interupting
if(!Running )//prevent multiple starts
{
listener = new StreamSocketListener();
listener.BindServiceNameAsync("8080");
listener.ConnectionReceived += listener_ConnectionReceived;
Running = true;
}
}
}
public void Stop()
{
Lock(this){//prevent any other code interupting
listener.Close();
listener.Dispose();
listener = null;
Running = false;
}
}
public void listener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
//process data from listerner's event
}
}
}
Note i'm not using the Windows Application Framework so might be different there

Hot to call a method from another thread or fire a event from another thread which gets handled on the main thread

I know, the title might be confusing, but I can't really express it better in short.
Basically, I'm writing a TCP Server. I don't have a Windows Form to show, the only thing the user sees is a TrayIcon.
My TCP Server class creates a thread for listening for clients, and then additional thread for every client handling communication. When all communication is done, I want to call a method on the main thread.
I've done it by firing a event from the client communication thread which gets handled on the main thread, and all worked fine until I wanted to add desktop notifications to my application. I've build a notification using WPF (iControlNotification) and wanted to show it in the event handler I mentioned before, but I'm getting a error message saying something like "The calling thread has to be a STA Thread".
Here's some code (I removed unnecessary party):
static class Program {
[...]
[STAThread]
static void Main() {
Log("iControlServerApplication started.");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
trayIcon = new TrayIcon();
trayIcon.Display();
NotificationManager = new iControlNotificationManager();
server = new TCPServer();
server.CommandReceived += new TCPServer.CommandReceivedEventHandler(tcpServer_CommandReceived);
if (server.Start()) {
NotificationManager.ShowNotfication("iControl Server Application", "Server started. " + plugins.Count + " plugins loaded.");
Application.Run();
} else {
MessageBox.Show("Port " + server.Port + " is already in use. Server could not be started.", ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
[...]
static void tcpServer_CommandReceived(object source, TCPServer.CommandReceivedEventArgs e) {
string toolTipText = "[" + e.Client.IPAddress + "] >> " + e.Command;
NotificationManager.ShowNotfication("iControl Server Application", toolTipText);
foreach (IiControlPlugin plugin in plugins) {
plugin.Handle(e.SplittedCommands, e.Client);
}
}
[...]
}
-
class TCPServer {
public delegate void CommandReceivedEventHandler(object source, CommandReceivedEventArgs e);
public event CommandReceivedEventHandler CommandReceived;
public class CommandReceivedEventArgs : EventArgs {
private string _command;
private string[] _splittedCommands;
private iControlClient _client;
public CommandReceivedEventArgs(string command, iControlClient client) {
_command = command;
_splittedCommands = command.Split(new Char[]{' '});
_client = client;
}
public string Command { get { return _command; } }
public string[] SplittedCommands { get { return _splittedCommands; } }
public iControlClient Client { get { return _client; } }
}
public TCPServer() {
this.tcpListener = new TcpListener(IPAddress.Any, Port);
this.icClients = new Dictionary<String, iControlClient>();
}
public Boolean Start() {
if (PortIsAvailable(Port)) {
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
Program.Log("ListeningThread started.");
return true;
} else {
return false;
}
}
private void ListenForClients() {
this.tcpListener.Start();
TcpClient client;
while (this.keepListening) {
try {
client = this.tcpListener.AcceptTcpClient();
} catch {
break;
}
iControlClient icClient = new iControlClient(client);
icClient.Thread = new Thread(new ParameterizedThreadStart(HandleClientCommunication));
icClient.Thread.Start(icClient);
}
Program.Log("Stop listening.");
}
private void HandleClientCommunication(object client) {
iControlClient icClient = (iControlClient)client;
NetworkStream clientStream = icClient.TCP.GetStream();
clientStream.ReadTimeout = 10;
int bufflen = 4096;
byte[] message = new byte[bufflen];
int bytesRead;
while (this.keepReceiving && icClient.keepConnected) {
bytesRead = 0;
try {
bytesRead = clientStream.Read(message, 0, bufflen);
} catch {
break;
}
if (bytesRead == 0) {
break;
}
ProcessReceivedData(icClient, ParseData(message, bytesRead));
}
Program.Log("[" + icClient.IPAddress + "] Connection closed.");
icClient.TCP.Close();
this.icClients.Remove(icClient.IPAddress);
}
private void ProcessReceivedData(iControlClient icClient, String[] commands) {
Program.Log("[" + icClient.IPAddress + "] >> " + String.Join(" ", commands));
if (this.CommandReceived != null) {
CommandReceived(this, new CommandReceivedEventArgs(String.Join(" ", commands), icClient));
}
NetworkStream clientStream = icClient.TCP.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("::ok");
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
icClient.keepConnected = false;
}
}
-
public class iControlNotificationManager {
private iControlNotifications _notifications;
public void ShowNotfication(string caption, string message) {
if ((Boolean)Program.GetSetting("notifications", true) == false) return;
Dispatcher.CurrentDispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(
() => {
iControlNotification noti = new iControlNotification(caption, message);
noti.Show();
}));
}
}
-
public class iControlNotification : Window {
private iControlNotificationModel _notification;
public iControlNotification(string caption, string message) { // Here's the error
InitializeComponent();
_notification = new iControlNotificationModel() {
Caption = caption,
Message = message
};
this.DataContext = _notification;
}
}
So how should I call tcpServer_CommandReceived so that the Notification Window can be shown the right way?
I'm really stuck here, I really appreciate any help on this!
//How to call a method from another thread :
a) You can invoke it in other thread by passing SynchronizationContext object to it:
void Method(object s)
{
SynchronizationContext sync = s as SynchronizationContext;
sync.Post(delegate { // what to do in other thread}, null);
}
Then in code you run this method in new task, passing your sync context as object (for example):
Task t = Task.Factory.StartNew(Method, SynchronizationContext.Current);
b) You can create extension method for this purpose (here is example that i used in win forms application to update UI):
public static class ControlExtensions
{
/// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.
public static void UIThread(this Control #this, Action code)
{
if (#this.InvokeRequired)
{
#this.BeginInvoke(code);
}
else
{
code.Invoke();
}
}
}

Categories

Resources