Im writing a simple client server app. On the client side I set up a singleton instance of class to do all communication with the server.
When new messages come in, I store them in a collection and then fire off an event. Any forms that are open and are subscribed to the event
will then receive the message and process it.
My problem is that if a form opens up after messages have been received, how do I send it the previously received messages and subscribe to
new ones without missing any messages, or getting one twice?
Form Code
private void Form_Load(object sender, EventArgs e)
{
handleMsgRec = new EventHandler<MsgEventArgs>(MsgReceived);
Comm.Instance.LoadMsgs(Msgs);
Comm.Instance.MsgReceived += handleMsgRec;
}
Server Communication Code
private static ReaderWriterLockSlim rw = new ReaderWriterLockSlim();
public void LoadMsgs(List<MsgObject> msgs)
{
rw.EnterReadLock();
Messages.ForEach(f => msgs.Add(f));
rw.ExitReadLock();
}
edit: I dont think i explained myself well enough. I already am storing all of the messages that I receive. My concern is that between the time I load the messages and subscribe to the event, that a new message could come in that I would miss. Or if I subscribe to the event first and then load the messages, that I could wind up receiving a message twice.
As Hans already mentioned: Store the previously received messages.
Additionaly you could use a property for the MsgReceived event. In the "add" part of the property you could call the added delegate for any previous messages.
public event EventHandler<MsgEventArgs> MsgReceived
{
add
{
foreach (MsgObject mo in msgs)
value(this, new MsgEventArgs() { Message = mo });
...
}
remove
{
...
}
}
Related
I am creating a windows service that is listening for messages in an MQ. My current code seems to be reprocessing the same message over and over again. I want to alter this code to only start listening to for messages After the current message is done processing
Here is my current code that keeps looping the same message(Processing of a message takes about 6 min)
protected override void OnStart(string[] args)
{
var maincode = new Thread(ProcessCode);
maincode.Name = "dud";
maincode.IsBackground = false;
maincode.Start();
}
void ProcessCode()
{
do
{
theListener.MessageReceived += new EventHandler<MessageReceivedEventArgs>(MQ_MessageReceived);
IBM.WMQ.MQMessage mqMessage = theListener.Listen("manager", "theQueue");
Thread.Sleep(5000);
} while (true);
}
If you are already receiving the message by using the Listen method. I'm not sure why you are hooking into the MessageReceived event. I would probably rename the MQ_MessageReceived event to just HandleMessage remove the MessageReceived Event Handler and just do something simple like.
void ProcessCode()
{
while(true) {
//assuming that the Listen method blocks and waits for a message to be received.
var message = theListener.Listen("manager, "theQueue");
HandleMessage(message);
}
}
also note that you probably want to put in some logic to be able to stop processing messages for when you need to shut the service down.
Is there a simple/clean/elegant way of having an event client be invoked at the time when it gets connected to an event? That is, when the client connects to the event using += such as:
someEvent += someClient
It would be useful if the client could be invoked 'right away' - as if the event had occurred just as the client is connecting. The purpose is to provide the client with the information from the most recent event that has occurred so the client can 'catch up' with the state associated with the event.
A concrete example: a store has some ticker-tape style signs in its windows. Each sign's code gets an event from the store's system telling it what to display (e.g., what's on sale at the moment). (Each sign displays the same thing.) When each sign gets turned on, it wants to know what the latest 'on sale' event was. If the store operator enters the sale info into the store system first (and the 'on sale' event goes out - to nobody), and then turns on the signs, the signs must somehow ask for the latest event's 'on sale' info. It seems cleaner to have the latest event (re)sent to a sign when it connects as an event client/receiver and thereby have just one mechanism in the sign.
Is there a way to connect a 'client has registered' hook to the event so that 'custom code' would be executed? In this case the code would send an event to (only) the new client so the client gets 'caught up'. For example, override the += operator used to register a client? (How to do so is not clear to me...)
Thanks for your ideas.
Try this:
class Test
{
public delegate void TestEventHandler(int arg);
private TestEventHandler _testEvent;
private readonly _defaultEventArg = 42;
public event TestEventHandler TestEvent
{
add
{
_testEvent += value;
value(_defaultEventArg);
}
remove
{
_testEvent -= value;
}
}
}
var t = new Test();
t.TestEvent += (val) =>
{
Console.WriteLine("Event fired: " + val);
};
You have to implement custom event accessors.
Have a look at this HowTo from the MSDN.
I copied the snippet and inserted a comment where you can insert you code to call the newly added listener:
event EventHandler IDrawingObject.OnDraw
{
add
{
lock (PreDrawEvent)
{
// Write your custom code here
PreDrawEvent += value;
}
}
remove
{
lock (PreDrawEvent)
{
PreDrawEvent -= value;
}
}
}
So, around a week ago I asked a question about activex and UDP. Here it is:
C# UDP Socket client and server
Now, I created two applications, one (the sender) to send pre-defined strings via UDP. The other is activex component that is called from a webpage, and it's thread is working in the background. Once an UDP message arrives, then it's doing it's stuff (writing in database, writing in log.txt, and so on).
The last thing i need is to return data (it's yet to be said if it will be string or something else). However, the method in the activex which is called must be a void, because if it's made to be string, the threading wont work, and only the first message will arrive.
My question is, how to do that? How to return data from a void function? For example, the web app now is calling the activex DLL like this:
ClassLibrary1.Class1 activex = new ClassLibrary1.Class1();
activex.StartThread();
And the StartThread() calls the listening thread and it's working in the background, and once UDP msg arrives, its doing some stuff like i said above.
How can i return value with the threads (events) and the web app will catch it and use it?
Thanks a lot.
You can use events (which implement the Observable pattern) to alert any listener that a new message has arrived:
public class NewMessageArgs : EventArgs
{
public string Message { get; private set; }
public NewMessageArgs(string message)
{
Message = message;
}
}
public class ActiveXComponent
{
public event EventHandler<NewMessageArgs> OnMessage;
public void StartThread()
{
while (true)
{
//do stuff
//raise "message received" event
if (OnMessage != null)
OnMessage(this, new NewMessageArgs("hi"));
}
}
}
You can then listen to these events like so:
ActiveXComponent activex = new ActiveXComponent();
activex.OnMessage += ProcessMessage;
activex.StartThread();
public void ProcessMessage(object sender, NewMessageArgs args)
{
var msg = args.Message;
//process
}
Basically you have to store some data in a spot where you can access it from both places (from the thread, and from the place where you started the thread). So you have a couple of options from the top of my head.
Store it in a database
Create a specific object (whatever type you need), and store it in a place where it is accessible from both places. For example, a singleton. A simpler better solution is to create a property on your ClassLibrary.Class1 class: set it from within the Class1-class, and get it from the place where you created an instance of your Class1-class.
Add an event to your Class1-class which fires when it is finished doing its job. And add some data to the EventArgs.
I'm assuming here you get notified when your thread is done doing whatever it is doing.
Edit: added events
The threading function can change the fields values of the class and you can access those fields, also your thread can fire events that other classes can subcribe to and then act on it.
Class1
{
private string value;
public string Value{get{return value;} set{value=value; FireTheEvent();}}
}
I have a client/server system on which the clients behave as slaves to the server. One of my forms has a listview which is populated by the processes running on the client. This same form can only be opened once, because on the second time I get an exception: InvalidOperationException Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
The form constructor:
public ProcessesForm(NetworkStream _stream)
{
InitializeComponent();
networkStream = _stream;
//event on which the process list will be processed
Server.OnProcessListingReceived += Server_OnProcessListingReceived;
//request client for process list
GetProcesses();
this.ActiveControl = textBox1;
}
Event handler:
private void Server_OnProcessListingReceived(object sender,
EventArgs.ProcessesEventArgs e)
{
//exception is thrown here
listView1.Invoke(new EventHandler(delegate { listView1.Items.Clear(); }));
processes = new List<ProcessesEventArgs.Process>(e.Processes);
foreach (var item in e.Processes)
{
ListViewItem lvi = new ListViewItem(item.Id);
lvi.SubItems.Add(item.Name);
lvi.SubItems.Add((Convert.ToInt64(item.Memory) / 1024).ToString());
listView1.Invoke(new EventHandler(delegate { listView1.Items.Add(lvi); }));
}
}
I am invoking the listview because the received list processing is done on the respective client thread.
As I stated on the beginning of the post everything works as it should on the first time the form is opened.
What I have already tried:
Create handle manually
Unassign the OnProcessListingReceived event
on form close.
Other things which didn't work and I really can't
recall.
What could be the cause of this problem?
EDIT:
As Hans Passant said the problem may be thread race, but if this was the case shouldn't lock solve the problem? I tried implementing lock, but didn't work:
lock(listView1)
{
listView1.Invoke(new EventHandler(delegate { listView1.Items.Clear(); }));
}
I'm working on a web application that shows event logs data, similar to Event Viewer. The application also has to provide a way for users to subscribe to event logs and receive notifications when an entry is written to subscribed logs, using a web service.
I'm using this code to subscribe to an event log in the web service:
EventLog eventLog = new EventLog(observer.logName, observer.machineName);
eventLog.EnableRaisingEvents = true;
eventLog.EntryWritten += new EntryWrittenEventHandler(eventLog_EntryWritten);
eventList.Add(eventLog);
I'm trying to use observers as subscribers to event logs, and when an EventWritten event is handled, call the Update method of one observer. The problem is, I don't know how to differentiate event logs, since they all use the same event handler. I'm doing this because the number of event logs differs from one machine to another. Also, I want observers to handle only one type of EventLog, ie. one observer would send an e-mail when an event is written to the Application log.
I use this line of code to get all logs on the current computer:
remoteEventLogs = EventLog.GetEventLogs(machineName);
EventWritten event handler has this object sender parameter, but Visual Studio shows its type as EventLogInternal, which I can't use, and I can't cast the sender to EventLog to get EventLog.Log property. If I try to cast it, like this:
void eventLog_EntryWritten(object sender, EntryWrittenEventArgs e)
{
var log = (EventLog)sender;
}
I get an exception saying I can't cast an EventLogInternal to EventLog.
Is there a way to know which EventLog fires the event?
Thanks
I think the problem is, that the whole concept of the EventLog class is that it assumes it works upon a single Log - which it does. So neither the EventWrittenEventArgs nor the EventEntry class sport a member that contains the Log-name, as it is implicitly given by the associated EventLog-instance. Bad is of course, that you cannot get to it inside the EventWritten-handler.
You could create a wrapper around System.Diagnostics.EventLog, like so:
class MyEventLog : EventLog
{
public MyEventLog(string logName, string machineName)
: base(logName, machineName)
{
base.EnableRaisingEvents = true;
base.EntryWritten += MyEventLog_EntryWritten;
}
void MyEventLog_EntryWritten(object sender, EntryWrittenEventArgs e)
{
Console.WriteLine("Entry in {0} log.", base.Log);
// Your code
}
}
Then use MyEventLogin places where you normally would use EventLog. Probably give it a better name though.
You could also factor out the "Your Code" part by providing an Action<string, EntryWrittenEventArgs> property that is being called from inside MyEventLog_EntryWritten and can be set to your "external" handler function.
Another option would be to use reflection along these lines:
string log = (string)sender.GetType().GetProperty("Log").GetValue(sender, null);
since sender in this case actually has the Log property.
I think that what you are looking for can be found in the EntryWrittenEventArgs.
The MSDN shows there is a property called Entry that shows you all kinds of information about what just got logged. There are some properties that might help you in the EventLogEntry class, such as MachineName or UserName.
Here is a link to the Args class
http://msdn.microsoft.com/en-us/library/system.diagnostics.entrywritteneventargs.aspx
Here is a link to the Entry class
http://msdn.microsoft.com/en-us/library/system.diagnostics.eventlogentry.aspx
I don't see a direct link to the specific event log, but if you poke around in that class with the debugger the Entry object might give you enough information to look it up.
I hope this helps some.
I agree with the idea of wrapping the EventLog class within another class as suggested by Christian. I recently worked on such a requirement.
This is the class that I created
public class EventLogWatcher : EventLog
{
Action<string, EntryWrittenEventArgs> _changeHandler;
public EventLogWatcher(string logName, Action<string, EntryWrittenEventArgs> changeHandler)
: base(logName)
{
_changeHandler = changeHandler;
}
public void EnableCapture()
{
base.EnableRaisingEvents = true;
base.EntryWritten += EventLogChangeHandler;
}
public void DisableCapture()
{
base.EnableRaisingEvents = false;
base.EntryWritten -= EventLogChangeHandler;
}
private void EventLogChangeHandler(object sender, EntryWrittenEventArgs e)
{
_changeHandler(base.Log, e);
}
}
Here is a usage
foreach (string eventlogType in LogTypes)
logWatchers.Add(new EventLogWatcher(eventlogType, EventLogChangeHandler));
foreach (EventLogWatcher localLog in logWatchers)
{
try
{
localLog.EnableCapture();
}
catch(Exception ex)
{
EventManager.PublishExceptionLogMessage(ex);
}
}
EventManager.PublishInfoLogMessage($"Started EventLog listeners for {string.Join(",", LogTypes)} logs");
private void EventLogChangeHandler(string eventLogType, EntryWrittenEventArgs e)
{
try
{
if (UploadAllowed(eventLogType, e))
{
Dm.EventLog model = _eventLogEntryMapper.MapEntryToModel(e);
Task.Factory.StartNew(
() => _eventLogUploadService.UploadEventLog(model),
_cancellationTokenProvider.Token,
TaskCreationOptions.None,
TaskScheduler.Default);
}
}
catch(Exception ex)
{
EventManager.PublishExceptionLogMessage(ex);
}
}
Another option would be to change the event-registration like this:
eventLog.EntryWritten += (sender, e) => eventLog_EntryWritten(eventLog, e);