I've looked at least a hundred threads and none gave me a solution to my specific problem so far and i got tired of looking,
I'm trying to emulate a runescape game server in C#,
I need to receive lots of connections and parse the requests and send out responses for them until the game has loaded all the files, and then proceed to the actual login protocol. this needs to be able to handle multiple connections per second, as in, i handle 3 basic protocols "jaggrab", "ondemand" and then regular game protocol i.e; login, player updating etc these are all handled on the same port
My Server class listens for connections on one thread, while on another thread my PipelineFactory handles the connections in a Queue one by one as fast as it can, meanwhile there is a TaskPool thread which is executing PoolableTask objects at their own individual intervals... this works fine except as soon as I accept a connection both the pool and server threads begin to block and furthermore, the server object stops listening for connections all together but the thread seems to keep running.. I assume this is because the TcpListener.Pending() is not being updated? but i cant seem to find a function to update this list in the docs or anywhere
I seem to be using the threads the way all the multithreading tutorials explain them to work? i dont really understand what im doing wrong.. heres the important parts of my code:
MainEntry.cs:
using System;
using System.Threading;
using gameserver.evt;
using gameserver.io;
using gameserver;
using gameserver.io.player;
public static class MainEntry
{
public static void Main(string[] args)
{
Console.WriteLine("Starting game server...");
new Thread(new ThreadStart(Server.Run)).Start();
new Thread(new ThreadStart(TaskPool.Run)).Start();
new Thread(new ThreadStart(PipelineFactory.Run)).Start();
//TODO maybe pool these
}
}
Server.cs:
using gameserver.io.player;
using gameserver.io.sql;
using gameserver.model;
using gameserver.model.player;
using util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using gameserver.io.player.pipeline;
using gameserver.evt;
using util.cache;
namespace gameserver.io
{
public class Server
{
public static TcpListener serverSocket;
public static Dictionary<int, Player> players = new Dictionary<int, Player>(Constants.MaxPlayers);
public static Database database;
public const int ListenPort = 43594;
public static bool running = true;
public static readonly Cache cache = new Cache(Constants.CacheDir);
public static void Bind()
{
serverSocket = new TcpListener(IPAddress.Parse("0.0.0.0"), ListenPort);
serverSocket.Start();
Console.WriteLine("Server started at 0.0.0.0:" + ListenPort);
}
public static void Run()
{
if (serverSocket == null)
Bind();
while(IsRunning())
{
Console.Write("serve");
var incoming = serverSocket.AcceptTcpClient();
if (incoming != null)
{
PipelineFactory.queue.Enqueue(new PlayerSocket(incoming));
}
Thread.Sleep(25);
}
}
private static void Destroy()
{
serverSocket.Stop();
//saveall players
//Environment.Exit(0);
}
public static bool Online(Player player)
{
if(player == null)
{
return false;
}
for (int i = 0; i < players.Count; i++)
{
if (player.username == players[i].username)
{
return true;
}
}
return false;
}
public static Database Database => database ?? (database = new Database());
public static bool IsRunning() => running;
}
}
TickPool.cs:
using gameserver.io;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace gameserver.evt
{
public class TaskPool
{
public static bool running = true;
public static List<PoolableTask> tasks = new List<PoolableTask>();
public static void Run()
{
do
{
long cur = Environment.TickCount;
Console.Write("tick");
foreach (PoolableTask t in tasks)
{
if (cur - t.last >= t.interval)
{
if (t == null)
continue;
t.Execute();
t.last = cur;
}
}
Thread.Sleep(100);
} while (Server.IsRunning());
}
public static void Add(PoolableTask task)
{
if(!tasks.Contains(task))
tasks.Add(task);
}
public static void Stop(PoolableTask task)
{
if(tasks.Contains(task))
tasks.Remove(task);
}
}
}
PipelineFactory.cs:
using gameserver.evt;
using gameserver.io.player.pipeline;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace gameserver.io.player
{
// simple but effective state machine for all non player model related protocol
public class PipelineFactory
{
public static Queue<PlayerSocket> queue = new Queue<PlayerSocket>(100);
private static readonly LoginPipe loginPipe = new LoginPipe();
private static readonly JaggrabPipe jgpipe = new JaggrabPipe();
private static readonly HandshakePipe handshake = new HandshakePipe();
private static readonly OnDemandPipe ondemand = new OnDemandPipe();
public static void Run()
{
while (Server.IsRunning())
{
Console.Write("pipe");
if (queue.Count() > 0)
{
var socket = queue.First();
if (socket == null || !socket.GetSocket().Connected
|| socket.state == PipeState.Play)
{
queue.Dequeue();
return;
}
switch (socket.state)
{
case PipeState.Handshake:
socket.currentPipeline = handshake;
break;
case PipeState.Jaggrab:
socket.currentPipeline = jgpipe;
break;
case PipeState.OnDemand:
socket.currentPipeline = ondemand;
break;
case PipeState.LoginResponse:
case PipeState.Block:
case PipeState.Finalize:
socket.currentPipeline = loginPipe;
break;
case PipeState.Disconnect:
//TODO: Database.saveForPlayer
socket.Close();
break;
}
try
{
if (socket.currentPipeline != null)
socket.state = socket.currentPipeline.HandleSocket(socket);
}
catch (Exception)
{
socket.Close();
queue.Dequeue();
}
}
Thread.Sleep(20);
}
}
}
}
The protocol itself really shouldn't matter just know it's all being handled asynchronously
I'm a java programmer at heart but trying to delve into C# head first so thats why my conventions might not be perfect, and I don't really know how to/dont understand intellisense documentation but at some point ill look into it
EDIT: I just wanna note that everything worked perfectly before i tried using threads to multithread this, when i had the PipelineFactory a PoolableTask object implementation, it could handle multiple connections etc and there was only the main thread calling 2 while loops handling everything in the whole server, i'm trying to spread out the load over the cpu but its not working out for me lol
Related
I am trying to use lock with a shared object which I pass into my worker thread. In the code below, if I pass in the syncLock object in the Execute method of Worker, everything works fine.
However, if I store a local copy of the syncLock object in my Worker class, it does not work.
Obviously when I'm doing the "_syncLock = syncLock;" assignment, instead of having a reference to the shared syncLock object, I'm getting a new object. So I end up with each thread having it's own syncLock now instead of the shared object.
Is there way to store a local reference to the shared object? I thought that an object assignment is always a "reference" in C#?
Worker.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class Worker
{
int _ID;
string _Request;
object _syncLock;
MyCache _TheCache;
public Worker(int ID, string Request, ref object syncLock, ref MyCache TheCache)
{
_ID = ID;
_Request = Request;
_syncLock = syncLock;
_TheCache = TheCache;
}
public void Execute()
{
lock (_syncLock)
{
if (_TheCache == null)
{
Thread.Sleep(2000);
_TheCache = new MyCache();
_TheCache.LoadCache();
Console.WriteLine("thread {0}: created and loaded the cache", _ID);
}
else
{
Console.WriteLine("thread {0}: using the existing cache", _ID);
Console.WriteLine("TheCache.MyCacheValue {0}", _TheCache.MyCacheValue);
Console.WriteLine("TheCache.CacheTimeStamp {0}", _TheCache.CacheTimeStamp);
}
}
Console.WriteLine("worker DoSomething: {0}", _Request);
}
}
}
Main.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class Main
{
public MyCache TheCache = null;
public object syncLock = new object();
public void Execute()
{
List<Task> TaskList = new List<Task>();
for (int i = 0; i < 10; i++)
{
Task _Task = new Task(() => DoSomething(i, "test"));
_Task.Start();
Console.WriteLine("started Thread={0} at {1}", _Task.Id, DateTime.Now.ToString("hh:mm:ss.fff tt"));
}
Console.WriteLine("Press any key to quit the program");
while (Console.ReadKey().KeyChar == 0) ;
}
void DoSomething(int ID, string Request)
{
Worker worker = new Worker(ID, Request, ref syncLock, ref TheCache);
worker.Execute();
}
}
}
MyCache.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class MyCache
{
public int MyCacheValue;
public DateTime CacheTimeStamp;
public MyCache()
{
}
public void LoadCache()
{
MyCacheValue = 1;
CacheTimeStamp = DateTime.Now;
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CacheConcurrency
{
class Program
{
static void Main(string[] args)
{
Main main = new Main();
main.Execute();
}
}
}
I worked out the problem, thanks for the comments who got me on the right track. In particular, thanks to MickyD who commented on the code needing to be thread-safe which led me to the answer.
It turns out that I was protecting with lock correctly in my Execute function, however in my constructor I was still referencing the shared Cache object without a lock, which was not thread-safe and causing a race condition.
The fix is to put both the constructor call and the Execute inside the critical section/lock block like so:
void DoSomething(int ID, string Request)
{
lock(syncLock)
{
Worker worker = new Worker();
worker.Execute(ID, Request, ref TheCache);
}
}
This also simplifies the worker code, avoiding the need to store a local reference of the syncLock object and doing a lock in there.
I also agree with MickyD's other comments that this code should be re-written to use async/await, not have a cache inside the worker etc. The longevity of us using this code base is in question, so investing in the tech debt is uncertain at the moment.
I'm accessing a web service, which has a limit of requests, you can make per minute. I have to access X > 10 entries, but am only allowed to make 10 per minute.
I realized the service as a Singleton, which can be accessed from different parts of the code. Now I need a way to know, how many request were made and whether I am allowed to make a new one.
Therefore I made a little sample code which adds 100 tasks. Each task has a delay of 3 seconds and a Task can only be executed when there haven't been ten tasks before by using Task.WhenAny. However I get an "An exception of type 'System.InvalidOperationException' occurred in mscorlib.dll but was not handled in user code" exception when I remove the completed task from the list.
How can I fix this?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Test
{
private static Test instance;
public static Test Instance
{
get
{
if (instance == null)
{
instance = new Test();
}
return instance;
}
}
private List<Task> taskPool = new List<Task>();
private Test()
{
}
public async void AddTask(int count)
{
// wait till less then then tasks are in the list
while (taskPool.Count >= 10)
{
var completedTask = await Task.WhenAny(taskPool);
taskPool.Remove(completedTask);
}
Console.WriteLine("{0}, {1}", count, DateTime.Now);
taskPool.Add(Task.Delay(TimeSpan.FromSeconds(3)));
}
}
}
A good old Sempahore solved my problem. This was a classic threading problem and there are a few tested concepts how to solve it and this is the one I'm using:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Test
{
private static Test instance;
public static Test Instance
{
get
{
if (instance == null)
{
instance = new Test();
}
return instance;
}
}
private static Semaphore _pool = new Semaphore(0, 10);
private Test()
{
_pool.Release(10);
}
public async void AddTask(int count)
{
_pool.WaitOne();
var task = Task.Delay(TimeSpan.FromSeconds(3));
Console.WriteLine("{0}, {1}", count, DateTime.Now);
await task;
_pool.Release();
}
}
}
Is it possible to write something in the console while the program is writing something in this console ? It can be useful when you rename, or remove some files, when you do a repetitive action, and the program is writing a lot in the console. Then you will be able to write a command to stop the execution of the repetitive action while the program is continuing to write in the console. I think it's not very clear, well I illustrated you this fact with the code which I think the most apt (but I precise that it doesn't work ;) ). We have 3 classes.
The main class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
private static bool m_Write;
public static bool write
{
get { return m_Write; }
set { m_Write = value; }
}
static void Main(string[] args)
{
int index = 0;
Console.ReadLine();
m_Write = true;
Reader reader = new Reader();
while (m_Write)
{
index++;
Writer writer = new Writer(index.ToString());
}
}
}
}
The reading class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication1
{
class Reader
{
private Thread m_Reading_Thread;
private string m_text_To_Read;
public Reader()
{
m_Reading_Thread = new Thread(new ThreadStart(Read));
m_Reading_Thread.Start();
}
public void Read()
{
m_text_To_Read = Console.ReadLine();
if (m_text_To_Read == "Stop")
{
Program.write = false;
}
}
}
}
And the writing class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication1
{
class Writer
{
private Thread m_Writing_Thread;
private string m_Text_To_Write;
public Writer(string text_To_Write)
{
m_Text_To_Write = text_To_Write;
m_Writing_Thread = new Thread(new ThreadStart(Write));
m_Writing_Thread.Start();
}
public void Write()
{
Console.WriteLine(m_Text_To_Write);
}
}
}
This isn't nearly as complicated as you're trying to make it. In general there are two ways you can do this. You can start a background thread to do the writing, and have the main thread block on the console waiting for the read, or you can have the main thread writing and have the background thread do the read. I like the first solution best:
public class Program
{
private static readonly ManualResetEvent StopWriting = new ManualResetEvent(false);
private static void Main(string[] args)
{
Thread t = new Thread(WriterFunc);
t.Start();
string input;
do
{
input = Console.ReadLine();
} while (input != "stop");
// Tell the thread to stop writing
StopWriting.Set();
// And wait for the thread to exit
t.Join();
}
private static void WriterFunc()
{
int index = 0;
while (!StopWriting.WaitOne(Timeout.Infinite))
{
++index;
Console.WriteLine(index.ToString());
}
}
}
Note that I used a ManualResetEvent here rather than a Boolean flag. An even better solution would be to use a CancellationToken. Using a flag can cause all kinds of interesting problems because the compiler might determine that the variable can't change (it assumes single-threaded access). Your thread might continue running even after the variable is changed.
If you want the main thread to do the writing, and the background thread to do the reading:
public class Program
{
private static readonly ManualResetEvent StopWriting = new ManualResetEvent(false);
private static void Main(string[] args)
{
Thread t = new Thread(ReaderFunc);
t.Start();
int index = 0;
while (!StopWriting.WaitOne(Timeout.Infinite))
{
++index;
Console.WriteLine(index.ToString());
}
// Wait for the background thread to exit
t.Join();
}
private static void ReaderFunc()
{
string input;
do
{
input = Console.ReadLine();
} while (input != "stop");
// Tell the main thread to stop writing
StopWriting.Set();
}
}
Something like this would work:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var w = new Writer();
var r = new Reader();
while (!r.finish)
{
w.enabled = true;
string k = Console.ReadKey(false).KeyChar.ToString();
w.enabled = false;
string line = k + Console.ReadLine();
r.Read(line);
}
}
}
class Writer
{
public bool enabled = true;
public Writer()
{
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (a, b) =>
{
if(enabled)
Console.WriteLine("Test");
};
timer.Start();
}
}
class Reader
{
public bool finish = false;
public void Read(string line)
{
if (line == "stop")
{
finish = true;
}
}
}
}
Don't worry if the Writer writes above what you are typing, the Console.ReadLine() only considers what you have typed.
In the case of a console application, no two threads can write data to the screen at the exact same time.
AFAIK, in the above answer, the Writes()'s constructor is continuously executed until it finishes running. Then the control will be passed to the Reader(). So I don't think that works for what you need. Correct me if I am wrong.
I have some big trouble with serial requests.
Description from what i want:
establish a serial connection, send serial requests to 6 temperature
sensors one by one (this is done every 0,5 second in a loop)
the question and answer-destination is stored in a List array
every request is started in a separate thread so the gui does not bug
while the programme waits for the sensor-hardware to answer
My problem:
The connection and the request is working fine, but if I am browsing data at the local hard drive the answer from the sensor-unit gets destroyed (negative algebraic sign or value from other sensor or simply wrong value).
How does this happen or how can I solve this?
Where I guess the problem might be:
In the private void ReceiveThread() of class SerialCommunication
Here is my code:
Class CommunicationArray:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hardwarecommunication
{
public class CommunicationArray
{
public string request { get; set; }
public object myObject { get; set; }
public string objectType { get; set; }
}
}
Class SerialCommunication
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.IO.Ports;
using System.Windows.Forms;
namespace Hardwarecommunication
{
class SerialCommunication
{
Thread t2;
Thread t;
private SerialPort serialPort = new SerialPort("COM2", 115200, Parity.Even, 8, StopBits.One);
string serialAnswer = "";
private volatile bool _shouldStop;
private int counter;
List<CommunicationArray> ar = new List<CommunicationArray>();
object[] o = new object[3];
public void addListener(string request, object myObject, string objectType)
{
CommunicationArray sa = new CommunicationArray();
sa.request = request;
sa.myObject = myObject;
sa.objectType = objectType;
ar.Add(sa);
}
public void startListen()
{
t2 = new Thread(() => writeSerialPortThread());
t2.Start();
}
public void startSerialPort2()
{
try
{
serialPort.Open();
//MessageBox.Show("Connection opend!");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
public void stopSerialPort2()
{
try
{
if (serialPort.IsOpen == true)
// Connection closed
serialPort.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void writeSerialPortThread()
{
string request = "";
for (int i = 0; i < ar.Count(); i++)
{
request = ar[i].request;
//request = ((object[])ar[0])[0].ToString();
//if (!t.IsAlive)
//{
try
{
t = new Thread(ReceiveThread);
_shouldStop = false;
//MessageBox.Show("start thread");
t.Start();
serialPort.Write(request);
Thread.Sleep(50);
_shouldStop = true;
t.Join();
}
catch
{
}
Label tmpLabelObject = (Label)ar[i].myObject;
serialAnswer = serialAnswer.Replace("=", "");
if (tmpLabelObject.InvokeRequired)
{
MethodInvoker UpdateLabel = delegate
{
tmpLabelObject.Text = serialAnswer;
};
try
{
tmpLabelObject.Invoke(UpdateLabel);
}
catch
{
}
}
}
}
private void ReceiveThread()
{
//MessageBox.Show("in thread");
while (!_shouldStop)
{
serialAnswer = "";
try
{
//MessageBox.Show("in thread");
serialAnswer = serialPort.ReadTo("\r");
if (serialAnswer != "")
{
}
return;
}
catch (TimeoutException) { }
}
}
}
}
Class Form1 //to establish the connection and to start the Sensor request
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Hardwarecommunication
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private SerialCommunication serialCommunication1 = new SerialCommunication();
private void Form1_Load(object sender, EventArgs e)
{
//start up serial connection
serialCommunication1.startSerialPort2();
}
private void buttonStart_Click(object sender, EventArgs e)
{
timerRecord.Enabled = true;
if (this.buttonStart.Text == "Start")
this.buttonStart.Text = "Stop";
else
this.buttonStart.Text = "Start";
}
private void timerRecord_Tick(object sender, EventArgs e)
{
if (this.buttonStart.Text == "Stop")
{
this.serialCommunication1.startListen();
}
}
private void buttonFillRequestArray_Click(object sender, EventArgs e)
{
this.serialCommunication1.addListener("$0BR00\r" + "\r", this.labelResult0, "label0"); //request to the hardware
this.serialCommunication1.addListener("$0BR01\r" + "\r", this.labelResult1, "label1");
this.serialCommunication1.addListener("$01R00\r" + "\r", this.labelResult2, "label2");
this.serialCommunication1.addListener("$01R01\r" + "\r", this.labelResult3, "label3");
this.serialCommunication1.addListener("$01R02\r" + "\r", this.labelResult4, "label4");
}
}
}
I woud be happy about any try to fix the problem.
I coud also upload the solution as .zip but you can't test it at all because you do not have the sensor hardware.
Note: serialPort.Write(string) is a non-blocking store into the output buffer.
That means the following won't guarantee you've even finished writing your request before you stop listening for a response:
serialPort.Write(request);
Thread.Sleep(50);
_shouldStop = true;
You could add:
while( serialPort.BytesToWrite > 0 ) Thread.Sleep(1); // force blocking
but it's ill advised.
One thing I'm wondering. There is only a single serial port here. Why do you want many different threads to work with it when you could manage the entire serial port interaction with a single thread? (Or at worse, 1 thread for input 1 thread for output)
To me it makes a lot more sense to store up requests into a queue of some kind and then peel them off one at a time for processing in a single thread. Responses could be similarly queued up or fired as events back to the caller.
EDIT: If you don't mind one read/write cycle at a time you could try:
string response;
lock(serialPort) {
// serialPort.DiscardInBuffer(); // only if garbage in buffer.
serialPort.Write(request);
response = serialPort.ReadTo("\r"); // this call will block till \r is read.
// be sure \r ends response (only 1)
}
My C# application uses the COM ports. I am having some difficulty that should be common to most programs. I need to get an event when the list of Portnames changes. I have a selection box where the user can choose from teh list of available port names. Does anyone have a snippet of code for this? Thank You.
It can also be done with help of "ManagementEventWatcher":
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Management;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
namespace HmxFlashLoader
{
/// <summary>
/// Make sure you create this watcher in the UI thread if you are using the com port list in the UI
/// </summary>
[Export]
[PartCreationPolicy(CreationPolicy.Shared)]
public sealed class SerialPortWatcher : IDisposable
{
public SerialPortWatcher()
{
_taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
ComPorts = new ObservableCollection<string>(SerialPort.GetPortNames().OrderBy(s => s));
WqlEventQuery query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent");
_watcher = new ManagementEventWatcher(query);
_watcher.EventArrived += (sender, eventArgs) => CheckForNewPorts(eventArgs);
_watcher.Start();
}
private void CheckForNewPorts(EventArrivedEventArgs args)
{
// do it async so it is performed in the UI thread if this class has been created in the UI thread
Task.Factory.StartNew(CheckForNewPortsAsync, CancellationToken.None, TaskCreationOptions.None, _taskScheduler);
}
private void CheckForNewPortsAsync()
{
IEnumerable<string> ports = SerialPort.GetPortNames().OrderBy(s => s);
foreach (string comPort in ComPorts)
{
if (!ports.Contains(comPort))
{
ComPorts.Remove(comPort);
}
}
foreach (var port in ports)
{
if (!ComPorts.Contains(port))
{
AddPort(port);
}
}
}
private void AddPort(string port)
{
for (int j = 0; j < ComPorts.Count; j++)
{
if (port.CompareTo(ComPorts[j]) < 0)
{
ComPorts.Insert(j, port);
break;
}
}
}
public ObservableCollection<string> ComPorts { get; private set; }
#region IDisposable Members
public void Dispose()
{
_watcher.Stop();
}
#endregion
private ManagementEventWatcher _watcher;
private TaskScheduler _taskScheduler;
}
}
COM ports changing is a rare event, not a common one.
The easiest way would be to have a timer and every 10-30 seconds enumerate the list of COM ports and if changed, update the list.
Better still, provide a "refresh list" button - the list will basically only change if the user has plugged a USB Serial adapter in.
Create a simple Form application and put the following code into the form:
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 537: //WM_DEVICECHANGE
var ports = SerialPort.GetPortNames().OrderBy(name => name);
foreach (var portName in ports)
{
Debug.Print(portName);
}
break;
}
base.WndProc(ref m);
}