So im trying to make a data-grid that displays some information about local window services, mine in particular, I would like to have the display name and status of the service, and then have a button to click to start or stop. I can link the button method up fine, but the service status does not change, any suggestions an how to make this property observable to the datagrid, and also possible change the button on the fly from start to stop based on the status, secondly I would like to make the stop command be be a button command if possibe.
Any suggestions?
You need to wrap your the service into your own class that implements INotifyPropertyChanged. As you start/stop the service, raise property change event on that instance.
This is what I ended up implamenting. It works pretty well for the most part, however I up for any code suggestions anyone might.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ServiceProcess;
namespace v7quickbar
{
class NotifiableServiceController : INotifyPropertyChanged
{
private ServiceController m_oServiceController = null;
private System.Timers.Timer m_oServiceCheckTimer = new System.Timers.Timer();
public ServiceControllerStatus Status { get { return this.m_oServiceController.Status; } }
public string DisplayName { get { return this.m_oServiceController.DisplayName; } }
public string ServiceName { get { return this.m_oServiceController.ServiceName; } }
public bool CanStop { get { return this.m_oServiceController.CanStop; } }
public NotifiableServiceController(ServiceController oService)
{
CreateObject(oService, TimeSpan.FromSeconds(.5));
}
public NotifiableServiceController(ServiceController oService, TimeSpan oInterval)
{
CreateObject(oService, oInterval);
}
private void CreateObject(ServiceController oService, TimeSpan oInterval)
{
m_oServiceController = oService;
m_oServiceCheckTimer.Interval = oInterval.TotalMilliseconds;
m_oServiceCheckTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_oServiceCheckTimer_Elapsed);
m_oServiceCheckTimer.Start();
}
public void Start()
{
try
{
this.m_oServiceController.Start();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Running);
}
catch (Exception)
{
}
}
public void Stop()
{
try
{
this.m_oServiceController.Stop();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Stopped);
}
catch (Exception)
{
}
}
public void Restart()
{
try
{
if (m_oServiceController.CanStop && (m_oServiceController.Status == ServiceControllerStatus.Running || m_oServiceController.Status == ServiceControllerStatus.Paused))
{
this.Stop();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Stopped);
}
if (m_oServiceController.Status == ServiceControllerStatus.Stopped)
{
this.Start();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Running);
}
}
catch (Exception)
{
}
}
void m_oServiceCheckTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
ServiceControllerStatus oCurrentStatus = m_oServiceController.Status;
m_oServiceController.Refresh();
if (oCurrentStatus != m_oServiceController.Status)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Status"));
}
}
public static IEnumerable<NotifiableServiceController> GetServices()
{
List<NotifiableServiceController> oaServices = new List<NotifiableServiceController>();
foreach (ServiceController sc in ServiceController.GetServices())
{
oaServices.Add(new NotifiableServiceController(sc));
}
return oaServices;
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Sadly because ServiceController.GetServices() call would always return an array, we have to have DispatcherTimer and in its tick, make call to ServiceController.GetServices() and raise notify property changed for that property that holds the array of services.
Making it observable for the sake of observability isnt practical right? We wont gain any advantage out of it anyways.
Related
There is a class library project that I use in many asp.net web application solutions.
In this project i have a class that i use for database operations.
Very simply it looks like as follows
(all codes are pseudo)
CoreDb.cs
{
public class cCoreDb()
{
string TableName;
Hashtable htFieldAndValue;
Hashtable htConditionFieldAndValue;
public int Insert()
{
...
}
public int Update()
{
...
}
public int Delete()
{
...
}
}
}
I use this class in solution's main project like as follows
WebForm1.cs
{
btnInsert_Click()
{
cCoreDb CoreDb = new cCoreDb();
CoreDb.TableName = "Table1";
CoreDb.htFieldAndValue.Add("Field1", "Value1");
CoreDb.htFieldAndValue.Add("Field2", "Value2");
CoreDb.Insert();
}
btnUpdate_Click()
{
cCoreDb CoreDb = new cCoreDb();
CoreDb.TableName = "Table1";
CoreDb.htFieldAndValue.Add("Field1", "Value1");
CoreDb.htFieldAndValue.Add("Field2", "Value2");
CoreDb.htConditionFieldAndValue.Add("ID", "3");
CoreDb.Update();
}
btnDelete_Click()
{
cCoreDb CoreDb = new cCoreDb();
CoreDb.TableName = "Table1";
CoreDb.htConditionFieldAndValue.Add("ID", "3");
CoreDb.Delete();
}
}
WebForm2.cs
{
btnInsert1_Click()
{
cCoreDb CoreDb = new cCoreDb();
CoreDb.TableName = "Table1";
CoreDb.htFieldAndValue.Add("Field1", "Value1");
CoreDb.htFieldAndValue.Add("Field2", "Value2");
CoreDb.Insert();
}
btnInsert2_Click()
{
cCoreDb CoreDb = new cCoreDb();
CoreDb.TableName = "Table2";
CoreDb.htFieldAndValue.Add("Field1", "Value1");
CoreDb.htFieldAndValue.Add("Field2", "Value2");
CoreDb.Insert();
}
}
WebForm3.cs
...
In all solutions which i use this class I want to catch these class methods calls (like a db trigger) in a central place (like global.asax or master page OR listen messages like WndProc)
Global.asax.cs OR Site.Master.cs OR another place in project
{
public class cCoreDbTrigger(cCoreDb CodeDb)
{
bool BeforeInsert()
{
if (CoreDb.TableName = "PRODUCT")
{
if (CoreDb.htFieldAndValue["CODE"] == already exists)
throw exception "product code already exists";
}
else if (CoreDb.TableName = "STOCK")
{
if (CoreDb.htFieldAndValue["NEW_STOCK"] < CriticalStock)
throw exception "invalid stock value";
}
else if (CoreDb.TableName = "XXX")
{
insert log_table;
}
...
}
}
bool AfterInsert()
{
...
}
bool BeforeUpdate()
{
...
}
bool AfterUpdate()
{
...
}
bool BeforeDelete()
{
...
}
bool AfterDelete()
{
...
}
}
How can I invoke before and after methods from coredb class for this like as follows
CoreDb.cs
{
public class cCoreDb()
{
public int Insert()
{
invoke cCoreDbTrigger.BeforeInsert(); //this message will be processed by the main project
do something;
invoke cCoreDbTrigger.AfterInsert(); //this message will be processed by the main project
}
}
}
notes:
i know that there may be better solutions for these scenarios. (like DB trigger, using entity objects, etc)
But i am trying to find a solution for the structure of my own application.
You could for example use events:
public class cCoreDb()
{
public event EventHandler BeforeInsert;
public event EventHandler AfterInsert;
public int Insert(Hashtable htFieldAndValue)
{
BeforeInsert?.Invoke();
do something;
AfterInsert?.Invoke();
}
}
...
public class cCoreDbTrigger(cCoreDb codeDb){
pubic cCoreDbTrigger(){
{
// attach event handlers
coreDb.BeforeInsert += BeforeInsert;
coreDb.EfterInsert += EfterInsert;
}
void BeforeInsert(object? sender, EventArgs e){
...
}
void EfterInsert(object? sender, EventArgs e){
...
}
}
Events allow for some other class to be informed when the event is triggered. You can also add parameters to the event if you so want.
Note that whenever you are using event handlers you should also keep in mind when they should be removed. A common cause of memory leaks is if you always add but never remove event handlers.
I have to access data from an event in another class.
In that class things are like this:
namespace MavLink
{
public class Mavlink
{
...
public event PacketReceivedEventHandler PacketReceived;
...
private void ProcessPacketBytes(byte[] packetBytes, byte rxPacketSequence)
{
...
if (PacketReceived != null)
{
PacketReceived(this, packet);
}
...
}
}
public delegate void PacketReceivedEventHandler(object sender, MavlinkPacket e);
}
And in the main I've tried to do like this:
...
m.ParseBytes(newlyReceived);
m.PacketReceived += (sender, e) => {
Console.WriteLine(e.SystemId);
Console.WriteLine(e.ComponentId);
Console.WriteLine(e.SequenceNumber);
Console.WriteLine(e.TimeStamp);
Console.WriteLine(e.Message);
};
But it doesn't seem work.
Thank you for your help.
Edit:
It compiles without errore but nothing is printed on the console. I don't know how to check if the event has been rised though.
Well on this what I read I have created a usual Event that will give you some data to access.
We start with creating the event.
public delegate void PacketReceivedEventHandler(var pPacket);
public event PacketReceivedEventHandler PacketReceived;
I put a var in there cause I didn't exactly saw what you are "delivering". Just change it into whatever you need.
So, lets continue. Place this when the Event needs to be triggered.
if (Mavlink.PacketReceived != null)
Mavlink.PacketReceived(YourPackage);
YourPackage is whatever you want to deliver.
But you need to subscribe a event to do stuff with it.
Mavlink.PacketReceived += Mavlink_PacketReceived;
C# usually created a class if you double tab after the +=. But here is the class I created.
private void Mavlink_PacketReceived(var pPacket)
{
if(pPacket != null)
{
Console.WriteLine(pPacket.SystemId);
Console.WriteLine(pPacket.ComponentId);
Console.WriteLine(pPacket.SequenceNumber);
Console.WriteLine(pPacket.TimeStamp);
Console.WriteLine(pPacket.Message);
}
}
I dont know what comes after that in your code, but make sure that there will be something to make you command line wait so it wont close after firing that.
I made an example, which works fine, hope it helps. Replace EventHandler by your PacketRecievedEventHandler:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var sender = new Sender();
var reciever = new Reciever(sender);
sender.ProcessPacketBytes(null, byte.MaxValue);
Console.ReadLine();
}
}
/// <summary></summary>
public class Sender
{
private readonly object _objectLock = new object();
public event EventHandler PacketReceived
{
add
{
lock (_objectLock)
{
PacketRecievedEvent += value;
}
}
remove
{
lock (_objectLock)
{
PacketRecievedEvent -= value;
}
}
}
private event EventHandler PacketRecievedEvent;
public void ProcessPacketBytes(byte[] packetBytes, byte rxPacketSequence)
{
EventHandler handler = this.PacketRecievedEvent;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
public class Reciever
{
public Reciever(Sender sendertest)
{
sendertest.PacketReceived += (sender, e) =>
{ Console.WriteLine(e.GetType()); };
}
}
}
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 was given a generic API class, that contains a custom event which always needs to be invoked by the main UI thread.
My job is to banish these invocation call from the custom class, to make it "painless".
It should be synchronized like the default events in WinForms (eg the Timer "Elapsed" event, which also needs no invocation when it published values to a text box)
Is it possible to solve this, since the custom class needs to know where to invoke?
Here's the (important part of the) code:
public class ContactSensorHelper
{
public event OnReleaseStateChanged ReleaseStateChanged;
public delegate void OnReleaseStateChanged(ContactSensorEventArgs e);
private ContactSensorEventArgs.ReleaseState recentReleaseState;
public void ReportStateChanged()
{
if (ReleaseStateChanged != null)
ReleaseStateChanged(new ContactSensorEventArgs()
{
State = recentReleaseState
});
}
public class ContactSensorEventArgs : EventArgs
{
//......
public ReleaseState State { get; set; }
//......
public enum ReleaseState
{
FullReleased,
PartlyReleased,
NotReleased
}
}
}
The call from main UI:
public void SensorInit()
{
//....
sensorHelper.ReleaseStateChanged += releaseStateChanged;
//....
}
private void releaseStateChanged(ContactSensorEventArgs e)
{
//example
textBox1.Text = e.State.ToString(); // Thread exception (obviously)
}
Does anybody have me a hint to start?
You could do this by using your own event calling, and storing a reference to the thread, when the event is attached.
With the event add/remove syntax, you can have the caller attach to the event like before, but internally you store a list, with a reference to the thread (using an AsyncOperation) and the delegate to be called (used a Tuple containing both in the example)
Below is an example. I tested it, and it worked as expected when testing, but you might have to add some locking of the list to make it thread safe in case events are added/removed simultaneously.
public class ContactSensorHelper:IDisposable
{
public delegate void OnReleaseStateChanged(ContactSensorEventArgs e);
private ContactSensorEventArgs.ReleaseState recentReleaseState;
public void ReportStateChanged()
{
if (statechangedList.Count > 0)
{
var e = new ContactSensorEventArgs()
{
State = recentReleaseState
};
statechangedList.ForEach(t =>
t.Item1.Post(o => t.Item2((ContactSensorEventArgs)o), e));
}
}
List<Tuple<AsyncOperation, OnReleaseStateChanged>> statechangedList = new List<Tuple<AsyncOperation,OnReleaseStateChanged>>();
public event OnReleaseStateChanged ReleaseStateChanged
{
add
{
var op = AsyncOperationManager.CreateOperation(null);
statechangedList.Add(Tuple.Create(op, value));
}
remove
{
var toremove = statechangedList.Where(t => t.Item2 == value).ToArray();
foreach (var t in toremove)
{
t.Item1.OperationCompleted();
statechangedList.Remove(t);
}
}
}
public void Dispose()
{
statechangedList.ForEach(t => t.Item1.OperationCompleted());
statechangedList.Clear();
}
public class ContactSensorEventArgs : EventArgs
{
//......
public ReleaseState State { get; set; }
//......
public enum ReleaseState
{
FullReleased,
PartlyReleased,
NotReleased
}
}
}
I have a ListBox that is bound to a BindingList. The BindingList is built up when a third party application raises an event. I can see the BindingList being bound correctly... but nothing enters the ListBox. I have used the exact same logic with some of my own custom types and it usually works very well.
Form class
private Facade.ControlFacade _controlFacade;
public UavControlForm()
{
InitializeComponent();
_controlFacade = new UavController.Facade.ControlFacade();
UpdateEntityListBox();
}
private void UpdateEntityListBox()
{
lsbEntities.DataSource = _controlFacade.GetEntityTally();
lsbEntities.DisplayMember = "InstanceName";
}
Facade class
private Scenario _scenario;
public ControlFacade()
{
_scenario = new Scenario();
}
public BindingList<AgStkObject> GetEntityTally()
{
BindingList<AgStkObject> entityTally = _scenario.EntityTally;
return entityTally;
}
Scenario class
private static BindingList<IAgStkObject> _entityTally = new BindingList<AgStkObject>();
public Scenario()
{
if (UtilStk.CheckThatStkIsAvailable())
{
UtilStk.StkRoot.OnStkObjectAdded += new IAgStkObjectRootEvents_OnStkObjectAddedEventHandler(TallyScenarioObjects);
UtilStk.StkRoot.OnStkObjectDeleted += new IAgStkObjectRootEvents_OnStkObjectDeletedEventHandler(TallyScenarioObjects);
}
}
private void TallyScenarioObjects(object sender)
{
List<AgStkObject> tallyOfStkObjects = UtilStk.GetRunningTallyOfAllStkObjects();
List<string> stkObjectNames = UtilStk.GetInstanceNamesOfStkObjects(tallyOfStkObjects);
foreach (string stkObjectName in stkObjectNames)
{
if (!SearchFlightUavTallyByName(stkObjectName))
{
if (!SearchLoiterUavTallyByName(stkObjectName))
{
if (!SearchEntityTallyByName(stkObjectName))
{
int i = stkObjectNames.IndexOf(stkObjectName);
_entityTally.Add(tallyOfStkObjects[i]);
}
}
}
}
}
I can see the event fire from the third-party application - this adds an entity to _entityList as desired, but noothing is added to lsbEntities - why?
(jump right to the last example if you want to see it fixed etc)
Threads and "observer" patterns (such as the data-binding on winforms) are rarely good friends. You could try replacing your BindingList<T> usage with the ThreadedBindingList<T> code I used on a previous answer - but this combination of threads and UI is not an intentional use-case of winforms data-binding.
The listbox itself should support binding via list notification events (IBindingList / IBindingListView), as long as they arrive form the right thread. ThreadedBindingList<T> attempts to fix this by thread-switching on your behalf. Note that for this to work you must create the ThreadedBindingList<T> from the UI thread, after it has a sync-context, i.e. after it has started displaying forms.
To illustrate the point that listbox does respect list-change notifications (when dealing with a single thread):
using System;
using System.ComponentModel;
using System.Windows.Forms;
class Foo
{
public int Value { get; set; }
public Foo(int value) { Value = value; }
public override string ToString() { return Value.ToString(); }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using(var form = new Form())
using (var lst = new ListBox())
using (var timer = new Timer())
{
var data = new BindingList<Foo>();
form.Controls.Add(lst);
lst.DataSource = data;
timer.Interval = 1000;
int i = 0;
timer.Tick += delegate
{
data.Add(new Foo(i++));
};
lst.Dock = DockStyle.Fill;
form.Shown += delegate
{
timer.Start();
};
Application.Run(form);
}
}
}
and now with added threading / ThreadedBindingList<T> (it doesn't work with the regular BindingList<T>):
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class Foo
{
public int Value { get; set; }
public Foo(int value) { Value = value; }
public override string ToString() { return Value.ToString(); }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using(var form = new Form())
using (var lst = new ListBox())
{
form.Controls.Add(lst);
lst.Dock = DockStyle.Fill;
form.Shown += delegate
{
BindingList<Foo> data = new ThreadedBindingList<Foo>();
lst.DataSource = data;
ThreadPool.QueueUserWorkItem(delegate
{
int i = 0;
while (true)
{
data.Add(new Foo(i++));
Thread.Sleep(1000);
}
});
};
Application.Run(form);
}
}
}
public class ThreadedBindingList<T> : BindingList<T>
{
private readonly SynchronizationContext ctx;
public ThreadedBindingList()
{
ctx = SynchronizationContext.Current;
}
protected override void OnAddingNew(AddingNewEventArgs e)
{
SynchronizationContext ctx = SynchronizationContext.Current;
if (ctx == null)
{
BaseAddingNew(e);
}
else
{
ctx.Send(delegate
{
BaseAddingNew(e);
}, null);
}
}
void BaseAddingNew(AddingNewEventArgs e)
{
base.OnAddingNew(e);
}
protected override void OnListChanged(ListChangedEventArgs e)
{
if (ctx == null)
{
BaseListChanged(e);
}
else
{
ctx.Send(delegate
{
BaseListChanged(e);
}, null);
}
}
void BaseListChanged(ListChangedEventArgs e)
{
base.OnListChanged(e);
}
}