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);
}
Related
Im traying to write a program to read data from a old AND scale using a serial to USB converter cable to display in a textbox .i was able to write a program successfully to read data only from a RS232 serial cable, but when I attached a serial to USB cable to it, it only displayed some numbers and others are just question marks. (Ex: ???0.3?2?)
method i used to read data .
private void PortOnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
while (_port.BytesToRead > 0)
{
// PostKeys
var original = _port.ReadExisting();
// Reformat string to fit SendKeys()
var reformattedString = DefaultFormatter.Reformat(original);
try
{
SendKeys.SendWait(reformattedString);
}
// Handle exception caused if keys are sent to an application
// not handling keys
catch(Exception ex)
{
}
}
}
Is that a problem that I can over come through a code or is that the serial to USB cable is malfunctioning ?
I tested the code below with a USB serial port device, which may also work with your scale. Some of the port settings were found by downloading/installing WinCT (RsCom, RsKey & RsWeight)). Then, in the Windows Start menu under A&D WinCT, select either RsCom or RsKey. Using RsCom or RsKey is an easy way to check that your USB cable/connection is working. I used both "RsKey" and "RsCom" with my USB serial device, and it seemed to work.
Create a WinForms project
VS 2017:
Open Visual Studio
Expand Installed
Expand Visual C#
Click Windows Desktop
Select Windows Forms App (.NET Framework)
Specify project name (name: ReadSerialPort)
Click OK
VS 2019:
Open Visual Studio
Click Continue without code
Click File
Select New
Select Project
C# Windows Desktop
Click Windows Forms App (.NET Framework)
Click Next
Specify project name (name: ReadSerialPort)
Click Create
Note: From this point forward, the process is the same for both VS 2017 and VS 2019.
Add class: SerialPortDataReceivedEventArgs
Note: This class will be used with an event that sends the data received from the serial port device to a subscriber.
On VS menu, select Project
Select Add Class (name: SerialPortDataReceivedEventArgs.cs)
SerialPortDataReceivedEventArgs.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ReadSerialPort
{
public delegate void SerialPortDataReceivedEventHandler(object sender, SerialPortDataReceivedEventArgs e);
public class SerialPortDataReceivedEventArgs : System.EventArgs
{
public string Data { get; private set; } = string.Empty;
public SerialPortDataReceivedEventArgs(string data)
{
this.Data = data;
}
}
}
Add Reference to System.Management
In VS menu, select Project
Select Add Reference
Expand Assemblies
Check System.Management
Click OK
Add class: ComPorts
On VS menu, select Project
Select Add Class (name: ComPorts.cs)
ComPorts.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ReadSerialPort
{
public class ComPorts
{
public List<ComPortInfo> Ports { get; set; } = new List<ComPortInfo>();
}
public class ComPortInfo
{
public string Name { get; set; }
public string PortName { get; set; }
public ComPortInfo()
{
}
public ComPortInfo(string name, string portName)
{
this.Name = name;
this.PortName = portName;
}
}
}
Add class: HelperSerialPort
On VS menu, select Project
Select Add Class (name: HelperSerialPort.cs)
HelperSerialPort.cs
//if using .NET 5, install NuGet Package: System.IO.Ports
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Diagnostics;
using System.Management;
namespace ReadSerialPort
{
public enum PortBaudRate : int
{
Baud1200 = 1200,
Baud2400 = 2400,
Baud4800 = 4800,
Baud9600 = 9600,
Baud14400 = 14400,
Baud19200 = 19200,
Baud28800 = 28800,
Baud38400 = 38400
};
public class HelperSerialPort : IDisposable
{
public delegate void SerialPortErrorReceivedEventHandler(object sender, SerialErrorReceivedEventArgs e);
public event SerialPortDataReceivedEventHandler DataReceived;
public event SerialPortErrorReceivedEventHandler ErrorReceived;
private string _dataReceived = string.Empty;
public System.IO.Ports.SerialPort Port { get; private set; }
public HelperSerialPort()
{
//create new instance
Port = new SerialPort();
}
public string Connect(string comPort, PortBaudRate baudRate = PortBaudRate.Baud9600)
{
string portName = string.Empty;
string result = string.Empty;
if (String.IsNullOrEmpty(comPort))
{
System.Windows.Forms.MessageBox.Show("COM port not selected.", "Error - COM Port", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
return "Error: COM port not selected.";
}
try
{
if (Port == null)
{
//create new instance
Port = new SerialPort();
}
if (!Port.IsOpen)
{
Debug.WriteLine("opening port");
//create new instance
Port = new SerialPort(comPort);
//set properties
Port.BaudRate = (int)baudRate;
Port.Handshake = Handshake.None;
Port.Parity = Parity.Even; //Even,None,Odd supported
Port.DataBits = 7;
Port.StopBits = StopBits.One;
Port.ReadTimeout = 200;
Port.WriteTimeout = 50;
Port.DtrEnable = true; //enable Data Terminal Ready
Port.RtsEnable = true; //enable Request to send
//open port
Port.Open();
//subscribe to events
Port.DataReceived += Port_DataReceived;
Port.ErrorReceived += Port_ErrorReceived;
//set value
result = "Connected";
}
else
{
Debug.WriteLine("else");
}
}
catch(Exception ex)
{
result = "Error: (Connect) - " + ex.Message;
}
Debug.WriteLine("result: " + result);
return result;
}
public void Close()
{
Dispose();
}
public void Dispose()
{
if (Port != null)
{
if (Port.IsOpen)
{
Port.Close();
}
//unsubscribe from events
Port.DataReceived -= Port_DataReceived;
Port.ErrorReceived -= Port_ErrorReceived;
Port.Dispose();
Port = null;
}
}
public ComPorts GetComPortInfo()
{
ComPorts comPorts = new ComPorts();
SortedDictionary<string, string> comPortNameDict = new SortedDictionary<string, string>();
SortedDictionary<string, string> portDict = new SortedDictionary<string, string>();
string[] portNames = SerialPort.GetPortNames();
//get USB COM ports
using (var searcher = new ManagementObjectSearcher("SELECT * FROM WIN32_PnPEntity"))
{
ManagementObjectCollection pnpEntityItems = searcher.Get();
if (portNames != null && pnpEntityItems != null)
{
var props = pnpEntityItems.GetEnumerator();
foreach (ManagementBaseObject mbo in pnpEntityItems)
{
if (mbo != null)
{
object nameObj = mbo.GetPropertyValue("Name");
object pnpClassObj = mbo.GetPropertyValue("PNPClass");
if (nameObj != null && pnpClassObj != null)
{
if (pnpClassObj.ToString() == "Ports" && nameObj.ToString().ToLower().Contains("(com"))
{
string name = mbo.GetPropertyValue("Name").ToString().Trim();
//Debug.WriteLine("name: " + name);
string portName = string.Empty;
if (name.Contains("(") && name.Contains(")"))
{
portName = name.Substring(name.IndexOf("(") + 1, name.IndexOf(")") - name.IndexOf("(") - 1);
//Debug.WriteLine("Port Name: '" + portName + "'");
}
if (!portDict.ContainsKey(name))
{
//add to dictionary - ex: Voyager 1450g, COM1
portDict.Add(name, portName);
//add to dictionary - ex: COM1, Voyager 1450g
comPortNameDict.Add(portName, name);
}
}
}
}
}
}
}
//add any ports that aren't USB -- ie: RS-232 (serial) devices
//USB devices are already in the dictionary, so only add devices
//that don't already exist in the dictionary
if (portNames != null && portDict != null && comPortNameDict != null)
{
foreach (string name in portNames)
{
if (!comPortNameDict.ContainsKey(name))
{
//add to dictionary
portDict.Add(name, name);
}
}
}
foreach(KeyValuePair<string, string> kvp in portDict)
{
//add to list
comPorts.Ports.Add(new ComPortInfo(kvp.Key, kvp.Value));
}
return comPorts;
}
private void Port_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
Debug.WriteLine("Error: (sp_ErrorReceived) - " + e.EventType);
if (this.ErrorReceived != null)
{
ErrorReceived(this, e);
}
}
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
_dataReceived = Port.ReadExisting();
Debug.WriteLine("_dataReceived: " + _dataReceived);
if (this.DataReceived != null)
{
SerialPortDataReceivedEventArgs eventArgs = new SerialPortDataReceivedEventArgs(_dataReceived);
DataReceived(this, eventArgs);
}
}
public void SerialCmdSend(string data)
{
if (Port.IsOpen)
{
try
{
// Send the binary data out the port
byte[] hexstring = Encoding.ASCII.GetBytes(data);
//write to SerialPort
foreach (byte hexval in hexstring)
{
byte[] _hexval = new byte[] { hexval }; // need to convert byte to byte[] to write
Port.Write(_hexval, 0, 1);
System.Threading.Thread.Sleep(1);
}
}
catch (Exception ex)
{
Debug.WriteLine("Error: Failed to SEND" + data + "\n" + ex.Message + "\n");
}
}
else
{
Debug.WriteLine("Error: Port is not open. Please open the connection and try again.");
}
}
}
}
Note: You may need to install a USB driver. AND Driver Software.
Create an extension method that can be used with RichTextBox.
Create class (ControlExtensions)
See How to update a RichTextBox from BackgroundWorker using BeginInvoke
On VS menu, select Project
Select Add Class (name: ControlExtensions.cs)
ControlExtensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ReadSerialPort
{
public static class ControlExtensions
{
public static void Invoke(this Control control, Action action)
{
if (control.InvokeRequired) control.Invoke(new MethodInvoker(action), null);
else action.Invoke();
}
}
}
Next we'll add some controls and code to Form1.
Open Properties Window
In VS menu, select View
Select Properties Window
Open Solution Explorer
In VS menu, select View
Select Solution Explorer
In Solution Explorer, double-click Form1.cs to open the designer.
Add "Connect" button to Form1
In VS menu, select View
Select Toolbox
Select Button
Click on Form1 to add the button to the form
In Properties Window, for "button1", set (name): btnConnect; set Text: Connect
In Properties Window, click (Events). Double-click Click to add event handler to Form1.cs
Add "Disconnect" button to Form1
In VS menu, select View
Select Toolbox
Select Button
Click on Form1 to add the button to the form
In Properties Window, for "button1", set (name): btnDisconnect; set Text: Disconnect
In Properties Window, click (Events). Double-click Click to add event handler to Form1.cs
Add RichTextBox to Form1
In VS menu, select View
Select Toolbox
Select RichTextBox
Click on Form1 to add the button to the form
In Properties Window, for "richTextBox1", set (name): richTextBoxReceivedData
Add "Load" event handler to Form1
In Properties Window, for "Form1"", click (Events). Double-click Load to add event handler to Form1.cs
Add "FormClosing" event handler to Form1
In Properties Window, for "Form1"", click (Events). Double-click FormClosing to add event handler to Form1.cs
Modify Form1.cs code
In Solution Explorer, right-click Form1.cs
Select View Code
Option 1 (doesn't automatically detect when a USB device is plugged in/unplugged):
Form1.cs
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;
using System.Diagnostics;
namespace ReadSerialPort
{
public partial class Form1 : Form
{
private HelperSerialPort helperSerialPort = new HelperSerialPort();
private ComPorts comPorts = null;
public Form1()
{
InitializeComponent();
}
private void FrmMain_Load(object sender, EventArgs e)
{
//get COM port info
GetComPorts();
}
private void HelperSerialPort_DataReceived(object sender, SerialPortDataReceivedEventArgs e)
{
Debug.WriteLine("Data: " + e.Data);
richTextBoxReceivedData.Invoke(() =>
{
richTextBoxReceivedData.AppendText(e.Data);
richTextBoxReceivedData.Refresh();
});
}
private void btnConnect_Click(object sender, EventArgs e)
{
if (helperSerialPort.Port == null || !helperSerialPort.Port.IsOpen)
{
helperSerialPort.Connect("COM3", PortBaudRate.Baud9600);
helperSerialPort.DataReceived += HelperSerialPort_DataReceived;
}
}
private void btnDisconnect_Click(object sender, EventArgs e)
{
helperSerialPort.Dispose();
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (helperSerialPort != null && helperSerialPort.Port != null)
{
helperSerialPort.Dispose();
helperSerialPort = null;
}
}
private void GetComPorts()
{
//get COM port info
comPorts = helperSerialPort.GetComPortInfo();
foreach (ComPortInfo cpInfo in comPorts.Ports)
{
Debug.WriteLine("Name: '" + cpInfo.Name + "' PortName: '" + cpInfo.PortName + "'");
}
}
}
}
Option 2 (automatically detect when a USB device is plugged in/unplugged):
Note: Some of the code below is from: Check for device change (add/remove) events
Create class (UsbDeviceNotification)
On VS menu, select Project
Select Add Class (name: UsbDeviceNotification.cs)
UsbDeviceNotification.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace ReadSerialPort
{
public static class UsbDeviceNotification
{
public const int DbtDevicearrival = 0x8000; // system detected a new device
public const int DbtDeviceremovecomplete = 0x8004; // device is gone
public const int WmDevicechange = 0x0219; // device change event
private const int DbtDevtypDeviceinterface = 5;
private static readonly Guid GuidDevinterfaceUSBDevice = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED"); // USB devices
private static IntPtr notificationHandle;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr RegisterDeviceNotification(IntPtr recipient, IntPtr notificationFilter, int flags);
[DllImport("user32.dll")]
private static extern bool UnregisterDeviceNotification(IntPtr handle);
[StructLayout(LayoutKind.Sequential)]
private struct DevBroadcastDeviceinterface
{
internal int Size;
internal int DeviceType;
internal int Reserved;
internal Guid ClassGuid;
internal short Name;
}
/// <summary>
/// Registers a window to receive notifications when USB devices are plugged or unplugged.
/// </summary>
/// <param name="windowHandle">Handle to the window receiving notifications.</param>
public static void RegisterUsbDeviceNotification(IntPtr windowHandle)
{
DevBroadcastDeviceinterface dbi = new DevBroadcastDeviceinterface
{
DeviceType = DbtDevtypDeviceinterface,
Reserved = 0,
ClassGuid = GuidDevinterfaceUSBDevice,
Name = 0
};
dbi.Size = Marshal.SizeOf(dbi);
IntPtr buffer = Marshal.AllocHGlobal(dbi.Size);
Marshal.StructureToPtr(dbi, buffer, true);
notificationHandle = RegisterDeviceNotification(windowHandle, buffer, 0);
}
/// <summary>
/// Unregisters the window for USB device notifications
/// </summary>
public static void UnregisterUsbDeviceNotification()
{
UnregisterDeviceNotification(notificationHandle);
}
}
}
Then use the following code in Form1.cs:
Form1.cs
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;
using System.Diagnostics;
namespace ReadSerialPort
{
public partial class Form1 : Form
{
private HelperSerialPort helperSerialPort = new HelperSerialPort();
private ComPorts comPorts = null;
public Form1()
{
InitializeComponent();
}
private void FrmMain_Load(object sender, EventArgs e)
{
//get COM port info
GetComPorts();
}
private void HelperSerialPort_DataReceived(object sender, SerialPortDataReceivedEventArgs e)
{
Debug.WriteLine("Data: " + e.Data);
richTextBoxReceivedData.Invoke(() =>
{
richTextBoxReceivedData.AppendText(e.Data);
richTextBoxReceivedData.Refresh();
});
}
private void btnConnect_Click(object sender, EventArgs e)
{
if (helperSerialPort.Port == null || !helperSerialPort.Port.IsOpen)
{
helperSerialPort.Connect("COM3", PortBaudRate.Baud9600);
helperSerialPort.DataReceived += HelperSerialPort_DataReceived;
}
}
private void btnDisconnect_Click(object sender, EventArgs e)
{
helperSerialPort.Dispose();
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (helperSerialPort != null && helperSerialPort.Port != null)
{
helperSerialPort.Dispose();
helperSerialPort = null;
}
}
private void GetComPorts()
{
//use SynchronizationContext.Current with ThreadPool to avoid the following error:
//Transition into COM context...for this RuntimeCallableWrapper failed with the following error:
//An outgoing call cannot be made since the application is dispatching an input-synchronous call.
//Exception from HRESULT: 0x8001010D (RPC_E_CANTCALLOUT_INPUTSYNCCALL)
var sc = System.Threading.SynchronizationContext.Current;
System.Threading.ThreadPool.QueueUserWorkItem(delegate
{
//do work on threadpool
sc.Post(delegate
{
//get COM port info
comPorts = helperSerialPort.GetComPortInfo();
foreach (ComPortInfo cpInfo in comPorts.Ports)
{
Debug.WriteLine("Name: '" + cpInfo.Name + "' PortName: '" + cpInfo.PortName + "'");
}
}, null);
});
}
private void UsbDeviceAdded()
{
//ToDo: add desired code
Debug.WriteLine("Info: USB device added");
//get COM port info
GetComPorts();
}
private void UsbDeviceRemoved()
{
//ToDo: add desired code
Debug.WriteLine("Info: USB device removed");
//get COM port info
GetComPorts();
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == UsbDeviceNotification.WmDevicechange)
{
switch ((int)m.WParam)
{
case UsbDeviceNotification.DbtDeviceremovecomplete:
UsbDeviceRemoved();
break;
case UsbDeviceNotification.DbtDevicearrival:
UsbDeviceAdded();
break;
}
}
}
}
}
Update:
Here's some additional info that may be useful:
Open PowerShell and run the following:
Get-CimInstance -Namespace Root\Cimv2 -Query "Select * From Win32_SerialPort Where Name like '%COM%'"
Get-CimInstance -Namespace Root\Cimv2 -Query "Select * From Win32_SerialPortConfiguration"
Get-CimInstance -Namespace Root\Cimv2 -Query "Select * From Win32_PnPEntity where PnPClass = 'Ports' and Name like '%COM%'"
mode
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
How can i monitor windows services using c# and i also have to save those services name, started time and services end time using in a CSV file. If any new services started than it should automatically write services name, started time and services end time using in existing CSV file.
In case someone is looking for a solution to this in 2021, you can do this using a service controller, async task and the WaitForStatus() method:
Update: I realized my initial solution would not work so I rewrote it completely:
CLASS DEFINITION
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.ServiceProcess; // not referenced by default
public class ExtendedServiceController: ServiceController
{
public event EventHandler<ServiceStatusEventArgs> StatusChanged;
private Dictionary<ServiceControllerStatus, Task> _tasks = new Dictionary<ServiceControllerStatus, Task>();
new public ServiceControllerStatus Status
{
get
{
base.Refresh();
return base.Status;
}
}
public ExtendedServiceController(string ServiceName): base(ServiceName)
{
foreach (ServiceControllerStatus status in Enum.GetValues(typeof(ServiceControllerStatus)))
{
_tasks.Add(status, null);
}
StartListening();
}
private void StartListening()
{
foreach (ServiceControllerStatus status in Enum.GetValues(typeof(ServiceControllerStatus)))
{
if (this.Status != status && (_tasks[status] == null || _tasks[status].IsCompleted))
{
_tasks[status] = Task.Run(() =>
{
try
{
base.WaitForStatus(status);
OnStatusChanged(new ServiceStatusEventArgs(status));
StartListening();
}
catch
{
// You can either raise another event here with the exception or ignore it since it most likely means the service was uninstalled/lost communication
}
});
}
}
}
protected virtual void OnStatusChanged(ServiceStatusEventArgs e)
{
EventHandler<ServiceStatusEventArgs> handler = StatusChanged;
handler?.Invoke(this, e);
}
}
public class ServiceStatusEventArgs : EventArgs
{
public ServiceControllerStatus Status { get; private set; }
public ServiceStatusEventArgs(ServiceControllerStatus Status)
{
this.Status = Status;
}
}
USAGE
static void Main(string[] args)
{
ExtendedServiceController xServiceController = new ExtendedServiceController("myService");
xServiceController.StatusChanged += xServiceController_StatusChanged;
Console.Read();
// Added bonus since the class inherits from ServiceController, you can use it to control the service as well.
}
// This event handler will catch service status changes externally as well
private static void xServiceController_StatusChanged(object sender, ServiceStatusEventArgs e)
{
Console.WriteLine("Status Changed: " + e.Status);
}
You can list running services using ServiceController or ManagementObjectSearcher.
Here is a sample using the ManagementObjectSearcher :
using System.Management;
...
StringBuilder sb = new StringBuilder();
string format = "{0},{1},{2},{3},{4}";
// Header line
sb.AppendFormat(format, "DisplayName",
"ServiceName",
"Status",
"ProcessId",
"PathName");
sb.AppendLine();
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("SELECT * FROM Win32_Service");
foreach( ManagementObject result in searcher.Get() )
{
sb.AppendFormat(format, result["DisplayName"],
result["Name"],
result["State"],
result["ProcessId"],
result["PathName"]
);
sb.AppendLine();
}
File.WriteAllText(
#"C:\temp\ManagementObjectSearcher_services.csv",
sb.ToString()
);
For getting start and stop times it looks like you have to query the Windows Event Log.
This blog post show how you can monitor the event log to get notified when a service is stopped or started:
https://dotnetcodr.com/2014/12/02/getting-notified-by-a-windows-service-status-change-in-c-net/
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)
}
I am rewriting my last question, just because I am trying to attack the problem from differente angles..
The problem is this. I have a class written in C#, with two public methods, and an eventhandler that triggers everytime any of the methods is completed.
I created this class adapting the code from a Form.
Now, if I reference this class from a Windows Process project written in VB.Net, and I call any of the two methods, the EventHandler will NOT trigger
Could this problem be related to EventHandler's scope or anything like that?
If necessary, I can post code
Thanks
++++++++++++++++++++++++
UPDATE
++++++++++++++++++++++++
Ok.. here is the code. Originally I was calling the methods from a class, but I modified the whole project just to see if the problem had to do with trying to raise the event from the class... So I have this c# webserver listening on port 8080, and upon http request calls ENROLL or IDENTIFY according to URL parameters.
Just to clarify a bit. This is a webserver that will run in a computer which has a USB fingerprint scanner connected. Then, the web application will do an http request to that computer in order to execute an IDENTIFICATION or ENROLLMENT, actions which are programmed inside the webserver, manipulating the methods included in the scanner's driver which I got with the SDK.
In have a C# demo project which came with the driver's SDK, a simple form with buttons calling ENROLL or IDENTIFY methods from the CLICK event. After the CLICK event is finished (inside of which is executed the ENROLL or IDENTIFY methods) then the Event is triggered and the EventHandler executes.
So now I have all the code in the same project, but it is still behaving the same way... it goes into the ENROLL method which executes the StartEnroll method from the WisSensorNLibLib class, but the event is not triggered nor captured by the class' custom Event Handler...
Maybe I am misplacing the definitions or the instantiations... I don't know. But the event is not triggered...
So here is the code... (and below I will paste the original Demo program code, so maybe by comparison someone can find out what the problem could be)
namespace WinServer
{
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
//************************************
//This is the class that belongs to the Fingerprint Scanner
//************************************
using WisSensorNLibLib;
class WinServer
{
private TcpListener myListener ;
private int port = 8080 ; // Select any free port you wish
private Int16 id;
private string indx;
//************************************
//Create instance of WisSensorN
//************************************
WisSensorN WisObj = new WisSensorN();
private String mod;
private Int32 Rows;
public WinServer()
{
try
{
myListener = new TcpListener(IPAddress.Any, port) ;
myListener.Start();
Thread th = new Thread(new ThreadStart(StartListen));
th.Start() ;
}
catch(Exception e)
{
Console.WriteLine("An Exception Occurred while Listening :" +e.ToString());
}
}
public void SendHeader(string sHttpVersion, string sMIMEHeader, int iTotBytes, string sStatusCode, ref Socket mySocket)
{
//************************************
//Code not related to understand specific problem was removed
//************************************
}
public void SendToBrowser(String sData, ref Socket mySocket)
{
//************************************
//Code not related to understand specific problem was removed
//************************************
}
public void SendToBrowser(Byte[] bSendData, ref Socket mySocket)
{
//************************************
//Code not related to understand specific problem was removed
//************************************
}
// Application Starts Here..
public static void Main()
{
WinServer MWS = new WinServer();
}
public void StartListen()
{
//Call ENROLL method
Enroll();
mySocket.Close();
}
//**********************************
//EVENT CAPTURE
//**********************************
public void WisObj_DataEvent(WisSensorNLibLib.DATA data, string str)
{
switch (data)
{
//ENROLL
case DATA.DATA_ENROLL:
try
{
Console.WriteLine("success");
}
catch (Exception ee)
{
Console.WriteLine(ee.Message);
}
break;
//IDENTIFY
case DATA.DATA_IDENTIFY_CAPTURE:
try
{
Console.WriteLine("Success");
}
catch (Exception ee)
{
Console.WriteLine(ee.Message);
}
break;
}
}
//**********************************
//ENROLL
//After this method ends, WisObj_DataEvent should capture the event
//**********************************
public void Enroll()
{
try
{
WisObj.Open();
WisObj.DataEvent += new _IWisSensorNEvents_DataEventEventHandler(WisObj_DataEvent);
WisObj.StartEnroll();
}
catch
{
Console.WriteLine(ee.Message);
}
}
//**********************************
//IDENTIFY
//After this method ends, WisObj_DataEvent should capture the event
//**********************************
public void Identify()
{
try
{
WisObj.Open();
WisObj.DataEvent += new _IWisSensorNEvents_DataEventEventHandler(WisObj_DataEvent);
WisObj.IdentifyCapture();
}
catch
{
Console.WriteLine(ee.Message);
}
}
public void WisClose()
{
WisObj.Close();
}
}
}
Original Demo program code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using WisSensorNLibLib;
namespace OR200N_Demo
{
public partial class Form1 : Form
{
private const int MaxUser = 10;
WisSensorN WisObj = new WisSensorN();
string[] DB = new string[MaxUser];
int nEnrolled = 0;
string indx;
string msg;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show("Please make sure the scanner is plugged!!");
WisObj.Open();
WisObj.DataEvent += new _IWisSensorNEvents_DataEventEventHandler(WisObj_DataEvent);
WisObj.SetDisplay((int)FingerPic.Handle);
}
private delegate void CompleteHandler(string bir);
private void Complete(string str)
{
Status.Text = str;
}
private void WisObj_DataEvent(WisSensorNLibLib.DATA data, string str)
{
switch (data)
{
case DATA.DATA_ENROLL:
indx = nEnrolled.ToString();
msg = "User #" + indx + " is enrolled successfully!!";
this.Invoke(new CompleteHandler(Complete), new object[] { msg });
DB[nEnrolled] = string.Copy(str);
nEnrolled++;
break;
case DATA.DATA_IDENTIFY_CAPTURE:
int nMatched;
nMatched = WisObj.Identify(str, DB);
if (nMatched < 0)
{
msg = "No valid finger matched!!";
this.Invoke(new CompleteHandler(Complete), new object[] { msg });
}
else
{
indx = nMatched.ToString();
msg = "User #" + indx + " is matched!!";
this.Invoke(new CompleteHandler(Complete), new object[] { msg });
}
break;
case DATA.DATA_VERIFY_CAPTURE:
break;
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
WisObj.Close();
}
private void Enroll_Click(object sender, EventArgs e)
{
if (nEnrolled >= MaxUser)
{
MessageBox.Show("Exceed maximum enrolled number (10)!!");
}
else
{
Status.Text = "Please put your finger on the scanner!!";
WisObj.StartEnroll();
}
}
private void Identify_Click(object sender, EventArgs e)
{
Status.Text = "Please put your finger on the scanner!!";
WisObj.IdentifyCapture();
}
}
}
Firstly, it is event, not event handler, which is being raised.
Event handler is a method that subsribes to a given event.
Secondly, scope only governs member visibility at compile time and does not affect event subscriptions. If event is visible, it works.
From what you posted, one may only conclude that:
You may not subscribe to event in VB .NET code or may handle it incorrectly;
When called from VB .NET code, these methods may act differently (different parameters being passed? exeptions thrown?) and may not raise the event.
So far, this is all I can say until I see the code.
Can you trim it down to simplest reproducible case?