I implemented an observer pattern using events and delegates. The program is receiving and processing big amounts of data (around 3000 messages per second) but at some point, it starts sending messages with a delayed timestamp, which I am trying to fix. I have 3 main classes that do the job in my opinion:
public class MessageTracker : IObservable<MessageEventArgs>
{
private List<IObserver<MessageEventArgs>> observers;
public MessageTracker()
{
observers = new List<IObserver<MessageEventArgs>>();
}
private static readonly MessageTracker mInstance = new MessageTracker();
private static MessageTracker getInstance() => mInstance;
private class Unsubscriber : IDisposable
{
private List<IObserver<MessageEventArgs>> _observers;
private IObserver<MessageEventArgs> _observer;
public Unsubscriber(List<IObserver<MessageEventArgs>> observers, IObserver<MessageEventArgs> observer)
{
this._observers = observers;
this._observer = observer;
}
public void Dispose()
{
if (! (_observer == null)) _observers.Remove(_observer);
}
}
public IDisposable Subscribe(IObserver<MessageEventArgs> observer)
{
if (! observers.Contains(observer))
observers.Add(observer);
return new Unsubscriber(observers, observer);
}
public void MessageTrack(MessageEventArgs msg) {
observers.AsParallel().ForAll(observer =>
{
if (msg is null)
observer.OnError(new ArgumentException("MessageError."));
else
observer.OnNext(msg);
});
}
public void EndMessageTrans(){
foreach(var observer in observers.ToArray())
if (observers.Contains(observer))
observer.OnCompleted();
observers.Clear();
}
}
public class MessageReporter : IObserver<MessageEventArgs>
{
private IDisposable unsubscriber;
public MessageReporter()
{ }
public event EventHandler<MessageEventArgs> OnNextMessage;
public virtual void Subscribe(IObservable<MessageEventArgs> provider)
{
if (provider != null)
unsubscriber = provider.Subscribe(this);
}
public void OnCompleted()
{
this.Unsubscribe();
}
public void OnError(Exception error)
{
}
public void OnNext(MessageEventArgs value)
{
if (OnNextMessage != null)
{
OnNextMessage?.Invoke(this, value);
}
}
public virtual void Unsubscribe()
{
unsubscriber.Dispose();
}
}
public sealed class MessageDataWorker
{
private readonly bool mSubscribeAll;
private readonly IEnumerable<string> mMessages;
public MessageDataWorker(IEnumerable<string> messages)
{
mMessages = messages;
if ((mMessages?.Count() ?? 0) == 0)
mSubscribeAll = true;
}
public override void DoWork()
{
var messageReporter = new MessageReporter();
messageReporter.OnNextMessage += OnNewMessageReceived;
messageReporter.Subscribe(MessageTracker.GetInstance());
while (!mShouldStop.WaitOne(100)) ;
MessageReporter.Unsubscribe();
}
private void OnNewMessageReceived(object sender, MessageEventArgs e)
{
if (!mSubscribeAll && !mMessages.Contains(e.Message))
return;
string message = "Message|" +
$"{e.Time}|" +
$"{e.Text};
try
{
Console.WriteLine(message);
}
catch { }
}
}
What I am trying to achieve is skipping notifications or receiving data for X milliseconds after sending the last message and afterward send the newest received message. I tried sleeping the observers and the provider but it just increased the delay. I think I am missing something and any suggestion would be appreciated.
From what I can tell from your code you could write the three classes with this code:
var messageTrack = new Subject<MessageEventArgs>();
var query =
from e in messageTrack
where !mMessages.Contains(e.Message)
select $"Message|{e.Time}|{e.Text}";
query.Throttle(TimeSpan.FromMilliseconds(X)).Subscribe(Console.WriteLine);
You should never need to implement IObservable<> or IObserver<> yourself. It almost always ends in disaster.
The above code handles the throttling you wanted.
I am working on my own multithreading for my algorithm independed pathfinding for unity. However, when I am executing two the same class I get a memory leak and when only executing one instance I am having no issues. I really want to use at least two threads if it is necessary.
Below is the class I have issues with. Keep in mind, that two independend threads will have to execute parts of this script. AddJob can be called from the main unity thread but will most likely be called from another update thread for the agents.
namespace Plugins.PathFinding.Threading
{
internal class PathFindingThread
{
private Thread m_Worker;
private volatile Queue<CompletedProcessingCallback> m_CallbackQueue;
private volatile Queue<IAlgorithm> m_QueuedTasks;
internal int GetTaskCount
{
get
{
return m_QueuedTasks.Count;
}
}
internal PathFindingThread()
{
m_Worker = new Thread(Run);
m_CallbackQueue = new Queue<CompletedProcessingCallback>();
m_QueuedTasks = new Queue<IAlgorithm>();
}
private void Run()
{
Debug.Log("<b><color=green> [ThreadInfo]:</color></b> PathFinding Thread Started ");
try
{
while(true)
{
if (m_QueuedTasks.Count > 0)
{
IAlgorithm RunningTask = m_QueuedTasks.Dequeue();
RunningTask.FindPath(new IAlgorithmCompleted(AddCallback));
}
else
break;
}
Debug.Log("<b><color=red> [ThreadInfo]:</color></b> PathFinding Worker is idle and has been Stopped");
}
catch(Exception)
{
Debug.Log("<b><color=red> [ThreadInfo]:</color></b> PathFinding thread encountred an error and has been aborted");
}
}
internal void AddJob(IAlgorithm AlgorithmToRun)
{
m_QueuedTasks.Enqueue(AlgorithmToRun);
//Debug.Log("Added Job To Queue");
}
private void AddCallback(CompletedProcessingCallback callback)
{
m_CallbackQueue.Enqueue(callback);
}
private void Update()
{
if (m_CallbackQueue.Count > 0)
{
if (m_CallbackQueue.Peek().m_Callback != null) { }
m_CallbackQueue.Peek().m_Callback.Invoke(m_CallbackQueue.Peek().m_Path);
m_CallbackQueue.Dequeue();
}
if (m_Worker.ThreadState != ThreadState.Running && m_QueuedTasks.Count != 0)
{
m_Worker = new Thread(Run);
m_Worker.Start();
}
}
}
internal delegate void IAlgorithmCompleted(CompletedProcessingCallback callback);
internal struct CompletedProcessingCallback
{
internal volatile FindPathCompleteCallback m_Callback;
internal volatile List<GridNode> m_Path;
}
}
namespace Plugins.PathFinding
{
internal enum TypeOfNode
{
Ground,
Air
}
//used to store location information since array can only take rounded numbers
internal struct Position
{
internal int x;
internal int y;
internal int z;
}
internal class GridNode
{
internal Position M_PostitionInGrid { get; private set; }
internal Vector3 M_PostitionInWorld { get; private set; }
internal TypeOfNode M_type { get; private set; }
internal bool m_IsWalkable = true;
internal GridNode m_ParrentNode;
internal int Hcost;
internal int Gcost;
internal int Fcost { get { return Hcost + Gcost; } }
internal GridNode(Position postion , Vector3 WorldPosition)
{
M_PostitionInGrid = postion;
m_IsWalkable = true;
M_PostitionInWorld = WorldPosition;
}
}
}
internal delegate void FindPathCompleteCallback(List<GridNode> Path);
internal abstract class IAlgorithm
{
protected GridNode m_SavedStart;
protected GridNode m_SavedTarget;
protected List<GridNode> m_LocatedPath;
protected FindPathCompleteCallback m_Callback;
internal FindPathCompleteCallback GetCallback
{
get
{
return m_Callback;
}
}
protected PathFindingGrid m_grid;
internal abstract void FindPath(IAlgorithmCompleted callback);
protected abstract List<GridNode> CreatePath(PathFindingGrid Grid, GridNode Start, GridNode Target);
protected abstract List<GridNode> RetracePath(GridNode start, GridNode target);
}
namespace Plugins.PathFinding.Astar
{
internal class AstarFinder : IAlgorithm
{
//construction of the Algorithm
internal AstarFinder(GridNode start, GridNode target, FindPathCompleteCallback Callback)
{
m_SavedStart = start;
m_SavedTarget = target;
m_Callback = Callback;
m_LocatedPath = new List<GridNode>();
m_grid = PathFindingGrid.GetInstance;
}
//function to start finding a path
internal override void FindPath(IAlgorithmCompleted callback)
{
//running Algorithm and getting the path
m_LocatedPath = CreatePath(PathFindingGrid.GetInstance, m_SavedStart, m_SavedTarget);
callback.Invoke(
new CompletedProcessingCallback()
{
m_Callback = m_Callback,
m_Path = m_LocatedPath
});
}
//Algorithm
protected override List<GridNode> CreatePath(PathFindingGrid Grid, GridNode Start, GridNode Target)
{
if(Grid == null ||
Start == null ||
Target == null)
{
UnityEngine.Debug.Log("Missing Parameter, might be outside of grid");
return new List<GridNode>();
}
List<GridNode> Path = new List<GridNode>();
List<GridNode> OpenSet = new List<GridNode>();
List<GridNode> ClosedSet = new List<GridNode>();
OpenSet.Add(Start);
int Retry = 0;
while (OpenSet.Count > 0)
{
if(Retry > 3000 || Grid == null)
{
UnityEngine.Debug.Log("Path Inpossible Exiting");
break;
}
GridNode CurrentNode = OpenSet[0];
for (int i = 0; i < OpenSet.Count; i++)
{
if(OpenSet[i].Fcost < CurrentNode.Fcost || OpenSet[i].Fcost == CurrentNode.Fcost && OpenSet[i].Hcost < CurrentNode.Hcost)
{
CurrentNode = OpenSet[i];
}
}
OpenSet.Remove(CurrentNode);
ClosedSet.Add(CurrentNode);
if(CurrentNode == Target)
{
Path = RetracePath(CurrentNode,Start);
break;
}
GridNode[] neighbour = Grid.GetNeighbouringNodes(CurrentNode);
for (int i = 0; i < neighbour.Length; i++)
{
if (!neighbour[i].m_IsWalkable || ClosedSet.Contains(neighbour[i]))
continue;
int CostToNeighbour = CurrentNode.Gcost + Grid.GetDistance(CurrentNode, neighbour[i]);
if(CostToNeighbour < neighbour[i].Gcost || !OpenSet.Contains(neighbour[i]))
{
neighbour[i].Gcost = CostToNeighbour;
neighbour[i].Hcost = Grid.GetDistance(neighbour[i], Target);
neighbour[i].m_ParrentNode = CurrentNode;
if (!OpenSet.Contains(neighbour[i]))
OpenSet.Add(neighbour[i]);
}
}
Retry++;
}
return Path;
}
//retracing the path out of a node map
protected override List<GridNode> RetracePath(GridNode start, GridNode target)
{
List<GridNode> Output = new List<GridNode>();
GridNode current = start;
while(current != target)
{
Output.Add(current);
current = current.m_ParrentNode;
}
Output.Reverse();
return Output;
}
}
}
This shows the core of your code made thread safe.
internal class PathFindingThread
{
Task m_Worker;
ConcurrentQueue<CompletedProcessingCallback> m_CallbackQueue;
ConcurrentQueue<IAlgorithm> m_QueuedTasks;
internal int GetTaskCount
{
get
{
return m_QueuedTasks.Count;
}
}
internal PathFindingThread()
{
m_CallbackQueue = new ConcurrentQueue<CompletedProcessingCallback>();
m_QueuedTasks = new ConcurrentQueue<IAlgorithm>();
m_Worker = Task.Factory.StartNew(() =>
{
while (true)
{
IAlgorithm head = null;
if (m_QueuedTasks.TryDequeue(out head))
{
head.FindPath(new IAlgorithmCompleted(AddCallback));
}
else
{
Task.Delay(0);
}
}
});
}
internal void AddJob(IAlgorithm AlgorithmToRun)
{
m_QueuedTasks.Enqueue(AlgorithmToRun);
}
private void AddCallback(CompletedProcessingCallback callback)
{
m_CallbackQueue.Enqueue(callback);
}
private void Update()
{
CompletedProcessingCallback cb = null;
if (m_CallbackQueue.TryDequeue(out cb))
{
cb.m_Callback.Invoke(cb.m_Path);
}
}
}
Volatile is only good for changing the value of the field - not calling methods on a collection that is referenced by the field.
You propably do not need to have Volatile in CompletedProcessingCallback, but it depends where else this is used. Certainly having volatile on a struct field is a bad smell.
Resolve these thread issues first, then see if you still have the problem.
I have a form that has a button to get a method executed in another class.
Code on the form:
public delegate void CustomPreviewCreate();
public static event CustomPreviewCreate CustomPreviewCreate_Do;
private void CreatePreview()
{
if (CustomPreviewCreate_Do !=null)
{
CustomPreviewCreate_Do();
}
}
This event then gets handled in another class. What I would like to achieve is that I can feed back to the form some form of return value if the method correctly executed.
What I tried so far does not get me the result.
Here is the code:
public void Initialize()
{
SubAsstViewPartControl.CustomPreviewCreate_Do += SubAsstViewPartControl_CustomPreviewCreate_Do;
// this gives me a the compiler error that the return type is wrong
}
private bool SubAsstViewPartControl_CustomPreviewCreate_Do()
{
// do stuff
return false;
}
Is there any direct way to return value from an event handler or I need to use a separate static field to store the event result in?
Update:
Per #Jon's comment, which seemed the simplest to me, I added an answer below demonstrating the simplest approach.
The common approach is to encapsulate your value in the type of EventArgs your event expects. For example, the Framework's CancelEventArgs contains a settable bool Cancel property, allowing each CancelEventHandler to assign a value. The sender can then read the property after the event has been invoked. You could also use a container-like EventArgs class if you want to collect separate values from individual event handlers. For example:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class SingleValueEventArgs : EventArgs
{
public int Value { get; set; }
}
public class MultiValueEventArgs : EventArgs
{
private List<int> _values = new List<int>(); // Private to prevent handlers from messing with each others' values
public IEnumerable<int> Values
{
get { return _values; }
}
public void AddValue(int value) { _values.Add(value); }
}
public class Exposer
{
public event EventHandler<SingleValueEventArgs> WantSingleValue;
public event EventHandler<MultiValueEventArgs> WantMultipleValues;
public void Run()
{
if (WantSingleValue != null)
{
var args = new SingleValueEventArgs();
WantSingleValue(this, args);
Console.WriteLine("Last handler produced " + args.Value.ToString());
}
if (WantMultipleValues != null)
{
var args = new MultiValueEventArgs();
WantMultipleValues(this, args);
foreach (var value in args.Values)
{
Console.WriteLine("A handler produced " + value.ToString());
}
}
}
}
public class Handler
{
private int _value;
public Handler(Exposer exposer, int value)
{
_value = value;
exposer.WantSingleValue += exposer_WantSingleValue;
exposer.WantMultipleValues += exposer_WantMultipleValues;
}
void exposer_WantSingleValue(object sender, SingleValueEventArgs e)
{
Console.WriteLine("Handler assigning " + _value.ToString());
e.Value = _value;
}
void exposer_WantMultipleValues(object sender, MultiValueEventArgs e)
{
Console.WriteLine("Handler adding " + _value.ToString());
e.AddValue(_value);
}
}
class Program
{
static void Main(string[] args)
{
var exposer = new Exposer();
for (var i = 0; i < 5; i++)
{
new Handler(exposer, i);
}
exposer.Run();
}
}
}
Per Jon Skeet's comment, which seemed the simplest to me, the simplest approach seems to be as follows:
public delegate bool CustomPreviewCreate(); // here we declare a return type
public static event CustomPreviewCreate CustomPreviewCreate_Do;
private void CreatePreview()
{
if (CustomPreviewCreate_Do !=null)
{
bool returnval = CustomPreviewCreate_Do();
}
}
And then:
// the method is declared to return the same type
bool SubAsstViewPartControl_CustomPreviewCreate_Do()
{
// do stuff
return true; // return the value of the type declared
}
I am trying to create an event inside my class and handle it from static void main method.my event is triggered by a method named checkAge().But i have got an error like this :
Error1-An object reference is required for the non-static field,
method, or property 'Event.Program.m_AgeChecker(int)
I think i did all prats that i had to do,& i don't know what is the problem.
Code of my first class
class Mahmud
{
public Mahmud()
{
name = "mahmud";
age = 25;
}
private string name;
private int age;
public string Name
{
get{return name;}
set{name=value;}
}
public int Age
{
get { return age; }
set { age = value; }
}
public void checkAge()
{
AgeUpdate(age);
}
public delegate void AgeEventHandler(int mAge);
public event AgeEventHandler AgeUpdate;
}
Code of the second class
static void Main(string[] args)
{
Mahmud m = new Mahmud();
m.AgeUpdate += new Event.Mahmud.AgeEventHandler(m_AgeChecker(m.Age));
m.Age = 16;
m.checkAge();
m.Age = 27;
m.checkAge();
}
private void m_AgeChecker(int A)
{
if (A > 25)
{
Console.WriteLine("!");
}
else
{
Console.WriteLine("ok");
}
}
It looks like you are calling a non-static method from a static method. You will need to make the method static.
static void m_AgeChecker(int A)
{
if (A > 25)
{
Console.WriteLine("!");
}
else
{
Console.WriteLine("ok");
}
}
I have an desktop console application created in visual studio 2010.How do i convert it to windows service?. basically in debug mode i want it as normal app , but in release mode i want a service build output
You can do it this way:
namespace Program
{
static class Program
{
public static bool Stopped = false;
[STAThread]
static void Main(string[] args)
{
Interactive.Initialize();
Interactive.OnStopped += new Interactive.StopedDelegate(OnStopped);
Interactive.Title = Path.GetFileNameWithoutExtension(
Assembly.GetExecutingAssembly().Location);
if (args.Length == 0) Interactive.Run(RunProc);
else if (args[0] == "-svc") ServiceBase.Run(new Service());
}
public static void RunProc() { yourConsoleMain(); }
public static void OnStopped() { Stopped = true; exitFromMain(); }
}
public class Service : ServiceBase
{
public static string Name = Path.GetFileNameWithoutExtension(
Assembly.GetExecutingAssembly().Location);
public static string CmdLineSwitch = "-svc";
public static ServiceStartMode StartMode = ServiceStartMode.Automatic;
public static bool DesktopInteract = true;
public bool Stopped = false;
public Service() { ServiceName = Name; }
public void Start() { OnStart(null); }
protected override void OnStart(string[] args)
{
System.Diagnostics.EventLog.WriteEntry(
ServiceName, ServiceName + " service started.");
Thread thread = new Thread(MainThread);
thread.Start();
}
protected override void OnStop()
{
System.Diagnostics.EventLog.WriteEntry(
ServiceName, ServiceName + " service stopped.");
Stopped = true;
Application.Exit();
}
private void MainThread()
{
Interactive.Run(Program.RunProc);
if (!Stopped) Stop();
}
}
}
Let me explain this... Basically, in Main you define that your program starts as a service if it is started with argument '-svc'.
Put in RunProc() what you normally do in main(), and in OnStopped() event handler some code that will cause main() to exit.
Then, override ServiceBase and perform some basic start/stop service.
In Windows 7 and later you must explicitly define that your service can interact with desktop if you want to see some output. But there is another problem, console window cannot be shown. So I created this console simulator which can write and also read input.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
namespace ProgramIO.Control
{
public delegate void WriteDelegate(string value, int x, int y);
public delegate void ReadDelegate(out string value, bool readLine);
public delegate void EnableInputDelegate(bool enable);
public partial class InteractiveForm : Form
{
private delegate void ClearInputBufferDelegate();
public enum EIOOperation { None = 0, Write, Read }
private EventWaitHandle eventInvoke =
new EventWaitHandle(false, EventResetMode.AutoReset);
private EventWaitHandle eventInput =
new EventWaitHandle(false, EventResetMode.AutoReset);
private bool readLine = false;
private string inputBuffer = "";
private int inputPosition = 0;
private int inputBufferPosition = 0;
private EIOOperation IOOperation;
private int bufferSize = 0x10000;
private bool CaretShown = false;
private delegate object DoInvokeDelegate(Delegate method, params object[] args);
private delegate void SetTitleDelegate(string value);
private delegate void SetForegroundcolorDelegate(Color value);
public string Title {
get { return Text; }
set {
if (InvokeRequired) InvokeEx(
(SetTitleDelegate)delegate(string title) { Text = title; },
1000, new object[] { value });
else Text = value; }}
public Color ForegroundColor {
get { return ForeColor; }
set {
if (InvokeRequired) InvokeEx(
(SetForegroundcolorDelegate)delegate(Color color) { ForeColor = color; },
1000, new object[] { value });
else ForeColor = value; }}
public InteractiveForm()
{
InitializeComponent();
DoubleBuffered = true;
}
#region Asynchronous Methods
private bool InvokeEx(Delegate method, int timeout, params object[] args)
{
BeginInvoke((DoInvokeDelegate)DoInvoke, new object[] { method, args });
if (eventInvoke.WaitOne(timeout)) return true;
else return false;
}
private void EnableInput(bool enable)
{
if (InvokeRequired)
InvokeEx((EnableInputDelegate)DoEnableInput, 1000, new object[] { enable });
else DoEnableInput(enable);
}
private void ClearInputBuffer()
{
if (InvokeRequired)
InvokeEx((ClearInputBufferDelegate)DoClearInputBuffer, 1000, new object[0]);
else DoClearInputBuffer();
}
public void Write(string value, int x = -1, int y = -1)
{
lock (this) {
IOOperation = EIOOperation.Write;
if (InvokeRequired)
InvokeEx((WriteDelegate)DoWrite, 1000, new object[] { value, x, y });
else DoWrite(value, x, y);
IOOperation = EIOOperation.None; }
}
public string Read(bool readLine)
{
lock (this) {
EnableInput(true);
IOOperation = EIOOperation.Read; this.readLine = readLine; string value = "";
ClearInputBuffer(); eventInput.WaitOne();
object[] args = new object[] { value, readLine };
if (InvokeRequired) {
InvokeEx((ReadDelegate)DoRead, 1000, args); value = (string) args[0]; }
else DoRead(out value, readLine);
//inputPosition = textBox.Text.Length; inputBuffer = "";
ClearInputBuffer();
IOOperation = EIOOperation.None;
EnableInput(false);
return value;
}
}
#endregion //Asynchronous Methods
#region Synchronous Methods
protected override void OnShown(EventArgs e) { base.OnShown(e); textBox.Focus(); }
public object DoInvoke(Delegate method, params object[] args)
{
object obj = method.DynamicInvoke(args);
eventInvoke.Set();
return obj;
}
private void CorrectSelection()
{
if (textBox.SelectionStart < inputPosition) {
if (textBox.SelectionLength > (inputPosition - textBox.SelectionStart))
textBox.SelectionLength -= inputPosition - textBox.SelectionStart;
else textBox.SelectionLength = 0;
textBox.SelectionStart = inputPosition; }
}
protected void DoClearInputBuffer()
{
inputPosition = textBox.Text.Length; inputBuffer = "";
}
protected void DoEnableInput(bool enable)
{
if (enable) { textBox.ReadOnly = false; textBox.SetCaret(true); }
else { textBox.ReadOnly = true; textBox.SetCaret(false); }
}
protected void DoWrite(string value, int x, int y)
{
string[] lines = textBox.Text.Split(new string[] { "\r\n" }, StringSplitOptions.None);
string[] addLines = new string[0];
if (y == -1) y = lines.Length - 1;
if (lines.Length - 1 < y) addLines = new string[y - lines.Length - 1];
if (y < lines.Length) {
if (x == -1) x = lines[y].Length;
if (lines[y].Length < x)
lines[y] += new String(' ', x - lines[y].Length) + value;
else
lines[y] = lines[y].Substring(0, x) + value +
((x + value.Length) < lines[y].Length ?
lines[y].Substring(x + value.Length) : ""); }
else {
y -= lines.Length;
if (x == -1) x = addLines[y].Length;
addLines[y] += new String(' ', x - addLines[y].Length) + value; }
textBox.Text = (string.Join("\r\n", lines) +
(addLines.Length > 0 ? "\r\n" : "") + string.Join("\r\n", addLines));
textBox.Select(textBox.Text.Length, 0); textBox.ScrollToCaret();
inputBuffer = "";
}
protected void DoRead(out string value, bool readLine)
{
value = "";
if (readLine) {
int count = inputBuffer.IndexOf("\r\n");
if (count > 0) { value = inputBuffer.Substring(0, count); }}
else if (inputBuffer.Length > 0) {
value = inputBuffer.Substring(0, 1); }
inputBuffer = "";
}
private void textBox_TextChanged(object sender, EventArgs e)
{
if (IOOperation == EIOOperation.Read) {
inputBuffer = textBox.Text.Substring(inputPosition);
if (!readLine || inputBuffer.Contains("\r\n")) eventInput.Set(); }
if (textBox.Text.Length > bufferSize) { textBox.Text =
textBox.Text.Substring(textBox.Text.Length - bufferSize, bufferSize);
textBox.Select(textBox.Text.Length, 0); textBox.ScrollToCaret(); }
}
private void textBox_KeyDown(object sender, KeyEventArgs e)
{
if (IOOperation != EIOOperation.Read ||
(e.KeyCode == Keys.Back && inputBuffer.Length == 0))
e.SuppressKeyPress = true;
}
private void textBox_MouseUp(object sender, MouseEventArgs e)
{
CorrectSelection();
}
private void textBox_KeyUp(object sender, KeyEventArgs e)
{
if (!(IOOperation == EIOOperation.Read) ||
((e.KeyCode == Keys.Left || e.KeyCode == Keys.Up) &&
textBox.SelectionStart < inputPosition))
CorrectSelection();
}
private void InteractiveForm_FormClosing(object sender, FormClosingEventArgs e)
{
eventInput.Set();
lock (this) { }
}
#endregion //Synchronous Methods
}
public class InteractiveWindow : TextBox
{
[DllImport("user32.dll")]
static extern bool HideCaret(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool ShowCaret(IntPtr hWnd);
private delegate void SetCaretDelegate(bool visible);
private const int WM_SETFOCUS = 0x0007;
private bool CaretVisible = true;
public void SetCaret(bool visible)
{
if (InvokeRequired) Invoke((SetCaretDelegate)DoSetCaret, new object[] { visible });
else DoSetCaret(visible);
}
private void DoSetCaret(bool visible)
{
if (CaretVisible != visible)
{
CaretVisible = visible;
if (CaretVisible) ShowCaret(Handle);
else HideCaret(Handle);
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_SETFOCUS)
{
if (CaretVisible) { ShowCaret(Handle); }
else HideCaret(Handle);
}
}
}
}
namespace ProgramIO
{
using ProgramIO.Control;
public static class Interactive
{
public delegate void StopedDelegate();
public delegate void RunDelegate();
public static bool Initialized = false;
private static InteractiveForm frmIO = null;
private static Thread IOThread = null;
private static EventWaitHandle EventStarted =
new EventWaitHandle(false, EventResetMode.AutoReset);
public static string Title {
get { return frmIO.Title; }
set { frmIO.Title = value; } }
public static Color ForegroundColor {
get {return frmIO.ForeColor; }
set { frmIO.ForeColor = value; } }
public static event StopedDelegate OnStopped = null;
private static void form_Show(object sender, EventArgs e)
{
frmIO = sender as InteractiveForm;
EventStarted.Set();
}
private static void form_FormClosed(object sender, FormClosedEventArgs e)
{
lock (frmIO) {
frmIO = null;
Application.Exit(); }
}
public static void Initialize()
{
IOThread = new Thread(IOThreadProc);
IOThread.Name = "Interactive Thread"; IOThread.Start();
EventStarted.WaitOne();
Initialized = true;
}
public static void Run(RunDelegate runProc = null)
{
if (!Initialized) Initialize();
if (runProc != null) runProc();
Application.Run();
if (OnStopped != null) OnStopped();
}
public static void IOThreadProc()
{
InteractiveForm form = new InteractiveForm();
form.Shown += new EventHandler(form_Show);
form.FormClosed += new FormClosedEventHandler(form_FormClosed);
Application.Run(form);
}
public static void Write(string value, int x = -1, int y = -1)
{
if (frmIO != null) lock (frmIO) { frmIO.Write(value, x, y); }
}
public static void WriteLine(string value)
{
if (frmIO != null) lock (frmIO) {
Interactive.Write(value); Interactive.Write("\r\n"); }
}
public static int Read()
{
if (frmIO != null) lock (frmIO) {
string input = frmIO.Read(false);
if (input.Length > 0) return input[0]; }
return 0;
}
public static string ReadLine()
{
if (frmIO != null) lock (frmIO) { return frmIO.Read(true); }
else return "";
}
}
}
This last class, Interactive, actually serve as invoker for asynchronous methods, and it is used in Main() at the beginning.
You can skip this whole second section of code if you don't want to see console window when program is run as a windows service.
I have also created an Installer class for this, but it would be just to much code on this page.
EDIT: This InteractiveForm is actually a form with designer class, but very simple, consisting only of Form and EditBox inside filling its area.
Basicallly you need 3 projects in your solution:
Application itself
WinService for production
Console Application for test purposes
So your application must have some kind of Start() method with e.g. infinite loop that does all work and maybe Stop() method to stop processing.
Winservice project must contain class derived from ServiceBase, it'l have OnStartmethod that calls your application's Start and OnStop that calls application's Stop method.
Next, in console application you do pretty much same - calling Start method in console's entry point.
So far for debug you run your console app, and for release you publish your winservice project
Winservice class might look like:
upd
Winservice codez:
public class MyWinService : ServiceBase
{
IMyApplicationService _myApplicationService;
//constructor - resolve dependencies here
public MyWinService()
{
_myApplicationService = new MyApplicationService();
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
try
{
_myApplicationService.Start();
}
catch (Exception exception)
{
//log exception
}
}
protected override void OnStop()
{
base.OnStop();
try
{
_myApplicationService.Stop();
}
catch (Exception exception)
{
//log exception
}
}
}
Application service:
public class MyApplicationService : IMyApplicationService
{
public MyApplicationService()
{
//some initializations
}
public Start()
{
//do work here
}
public Stop()
{
//...
}
}