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();}}
}
Related
I am using delegates and events to pass data around in my application, but want the triggering of 1 event to pass different sets of data to different places.
Problem
I have a main class which performs some work. Once the work is complete, I want it to notify a number of other classes (including the UI) so that other actions can be carried out. The information that the other classes needs is different.
Example
Main.cs - Performs an action and wants to update the UI, send a text message and write to a log file.
UI updates a DataGrid so needs the individual fields.
The log file writer needs all the whole line as an array / List.
The text message code needs the Line, LineNumber and FileName, but as a tab delimited string.
The below all works correctly when I am only trying to update the UI, but when I try to send different information to different places then I run into errors.
I have tried creating different extensions of EventArgs, but if I try to declare 2 delegates with different signatures then I get an error message.
Any help would be much appreciated.
FrmMain.cs
Main main = new Main();
main.PatternFound += OnPatternFound;
main.DoSomeWork();
private void OnPatternFound(object source, LineEventArgs e)
{
UpdateDataGrid(e.Line, e.FileName, e.LineNumber);
}
private void UpdateDataGrid(string line, string file, int lineNumber)
{
if (InvokeRequired)
{
Invoke(new Action<string, string, int>(UpdateDataGrid), line, file, lineNumber);
}
else
{
dgResults.Rows.Add(line, file, lineNumber);
}
}
Main.cs
public delegate void PatternFoundEventHandler(object sender, LineEventArgs e);
public event PatternFoundEventHandler PatternFound;
protected virtual void OnPatternFound(string line, string fileName, int lineNumber)
{
PatternFound?.Invoke(this, new LineEventArgs { Line = line, FileName = fileName, LineNumber = lineNumber });
}
public void DoSomeWork()
{
//Finished my work
OnPatternFound(line, file, lineNumber);
}
LineEventArgs.cs
public class LineEventArgs : EventArgs
{
public string Line { get; set; }
public string FileName { get; set; }
public int LineNumber { get; set; }
}
What you are trying to achieve is possibly not a good approach for several reasons. Your decided to stick to events which means you decided to do what is called "inversion of control".
You implemented your main class in a way that says: "I am doing some sort of pattern matching and I will tell whoever is interested what I found and I am doing it in the way my event-args implementation defines." As a result it's now up to the subscribers to take this information as is or leave it.
Strictly spoken, what you said...
Performs an action and wants to update the UI, send a text message and write to a log file.
...is not what you implemented, because the main class does nothing of this. Anyway, in my opinion it is a viable solution to keep it event-driven, but then you would need to change your upstream code.
You could for example attach three different event handlers to this single event. Every handler would then have to transform the data in a format it needs: The logging handler would have to transform it to an array, the UI handler would have to concatenate it and so on.
The other alternative would be that you have one handler and dispatch to three different methods, like the OnPatternFound method would call a UpdateUI method and a Log method and so on.
Lastly you could also get rid of inversion of control and move your logic to the main class, but I would not recommend it! Your approach is good, but you should just not try to invoke the same event in three different ways. That's up to the subscribers.
I'm quite new to C# and certainly OOP concepts.. so forgive the stupidity of my question.
I have a system I wish to communicate with, It has a number of commands that can be called with an associated response. (Communication is done via TCP/IP or Serial) (I implemented an Interface with SendMessage so that I can use multiple transport mechanisms)
I want to create a method for each command and then expose these, which is simple enough. The device also lets say 'broadcasts' messages as well which I want to act on, so I was using an event handler for this which works well..
At the moment in the event handler I catch OK and ERROR style messages, but ideally I would like to also be able to send the command from the above method and catch an error and return a bool value based on the command.
Can anyone think of a way I can do something like this and point me in the right direction?
Thanks
David
You can use helper to wait for event. Some ugly code from past:
public class ComWait
{
ManualResetEvent _waitEvent;
SomeEvent _eventHandler;
public ComWait()
{
_waitEvent = new ManualResetEvent(false);
_eventHandler = new SomeEvent(Watch);
}
void Watch()
{
_waitEvent.Set();
}
public bool Wait(int time = 3000)
{
_waitEvent.Reset();
SomeEvent += _eventHandler;
bool result = _waitEvent.WaitOne(time, false);
SomeEvent -= _eventHandler;
return result;
}
}
Usage is
ComWait wait = new ComWait();
if(!wait.Wait())
return; // timeout
// process
It will simply block synchronous method until event is rised or timeout occurs. It should be easy to add parameters: to unblock on specific event and to pass event handler parameters back to caller.
Otherwise I would simply have method inside communication class to use as a blocker:
readonly object _waitLock = new object();
public void Wait()
{
lock (_waitLock)
if (!Monitor.Wait(_waitLock, 3000))
throw new TimeoutException("No communications");
}
Signal at same time as you rise event:
lock (_waitLock)
Monitor.PulseAll(_waitLock);
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
{
...
}
}
I'm trying to optimize my code to be called from both an UI-less commandline call or call it from the UI.
The problem is that I have is I have written the lets call It worker-code inside the Form-class.
Now I want to pull out that worker code into a separate class.
Lets make a small sample to make my needs clearer:
public partial class form1 :Form
{
void AddLogmessage(String msg)
{
// update an listview
ListViewItem item = new ListViewItem();
item.Text = msg;
// Add the item to the ListView
LogView.Items.Add(item);
}
// button on ui to start working
private void btnStartTestRun_Click(object sender, EventArgs e)
{
try
{
DoSomeWork();
}
catch(Exception ex)
{}
}
private void DoSomeWork()
{
// do some really generic hard work....
AddLogMessage("working");
// do some more generic long lasting hard work....
AddLogMessage("working goes on...");
// in case of an error throw Exception
}
Now I want to refcator the worker code to work outside the form class, but be able to report the things that happen to the UI (if there is one) or to call the workercode without UI and do other reportings to an different target (communicate with other library which reports the results to an server)
Something like this:
public void AutomaticTaskHandler()
{
string[] cmdline = Environment.GetCommandLineArgs();
Arguments args = new Arguments(cmdline);
if (args["automatic"] != null)
{
doSomeWork();
}
}
In this case I don't have to report the Messages to the UI, but send some other messages (NOT the same Messages!!) to an server.
So my question is how do I make this the best way not having to write the doSomeWork - code twice but be able to send only the messages which are in the current scene are needed?
I thought about Delegates and Events, but I'm not too familiar to this to make this work.
Any help will be appreciated.
Thanks Meister_Schnitzel
Basically, you would create an interface IMessageTarget with a method SendMessage. Your UI code would create an implementation of that interface that outputs the messages to the UI and your console code would create an implementation of that interface that sends the messages to a server. On calling the doWork method, you would supply an instance of IMessageTarget.
After much research I am still stumped. I have a serial port object which is reading data continuously. What I am able to do it generate the dataReceived event, communicate with the port, and output the received values to the debug window. So, I'm pretty sure it's all working physically. The problem is when I try to pass the serial port output to my original thread I get an error. It says I can't have thread cross talk (or something to that effect). I've been trying to use a backgroundWorker but I'm not sure that is the solution I want plus with my novice skills it's a little over my head. And I tried to use invoke but the method doesn't seem to be available. (I might be calling from the wrong object?) Anyway section is below.
namespace Photometer
{
class csRadiometerILT1700
{
//manufacturer specs for baud rate, databits, and stop bits
static string portName="COM1";
static int baudRate = 1200;
static int dataBits = 8;
//instantialize a serial port object for the Radiometer
private SerialPort RadiometerSerial = new SerialPort(portName, baudRate, Parity.None, dataBits, StopBits.One);
//constructor
//public csRadiometerILT1700(Form ParentForm, Chart outputChart)
public csRadiometerILT1700()
{
//two handshaking properties of the ILT1700. Handshaking is enabled and
//http://stackoverflow.com/questions/6277619/problem-reading-serial-port-c-net-2-0-to-get-weighing-machine-output
RadiometerSerial.Handshake= Handshake.RequestToSend;
RadiometerSerial.DtrEnable = true;
RadiometerSerial.DataReceived += new SerialDataReceivedEventHandler(RadiometerSerial_DataReceived);
}
public void openPort()
{
if (!RadiometerSerial.IsOpen)
{
RadiometerSerial.Open();
}
}
public void closePort()
{
RadiometerSerial.Close();
}
string RadiometerVoltageReadingString;
int RadiometerVoltageReadingInt;
private void RadiometerSerial_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//It's here that this.invoke()... cannot be called.
RadiometerVoltageReadingString= (RadiometerSerial.ReadExisting().ToString()); //y-value
Debug.Print(RadiometerVoltageReadingString.ToString());
makeRadioReadingDouble(RadiometerVoltageReadingString);
}
private void makeRadioReadingDouble(string inputVoltageString)
{
List<double> outputVoltageDouble=new List<double>(2);
if (!(inputVoltageString == "\r\n" || inputVoltageString == ""))
{
string[] voltageValAndExpo = inputVoltageString.Split(new string[] { "e", "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
for (int inCounter = 0; inCounter < voltageValAndExpo.Count(); inCounter=inCounter+2)
{
double voltageVal = Convert.ToDouble(voltageValAndExpo[inCounter]);
double voltageExpo = Convert.ToDouble(voltageValAndExpo[inCounter + 1]);
outputVoltageDouble.Add(Math.Pow(voltageVal, voltageExpo));
}
}
}
}
}
This is all called when I form loads with the code
csRadiometerILT1700 Radiometer;
...
Radiometer = new csRadiometerILT1700();
Radiometer.openPort();
Any insight is appreciated.
EDIT:
I altered my csRadiometerILT1700 constructor to
public csRadiometerILT1700(Form inputForm)
{
//inputForm.Invoke(
//two handshaking properties of the ILT1700. Handshaking is enabled and
//http://stackoverflow.com/questions/6277619/problem-reading-serial-port-c-net-2-0-to-get-weighing-machine-output
RadiometerSerial.Handshake= Handshake.RequestToSend;
RadiometerSerial.DtrEnable = true;
RadiometerSerial.DataReceived += new SerialDataReceivedEventHandler(RadiometerSerial_DataReceived);
inputForm.Invoke(DataReceived);
}
and declare
public event Delegate DataReceived;
in the csRadiometerILT1700 class. But this gives me the error of "Datareceived must be of a delegate type." How do I resolve this now? Am I on the right track?
Your RadiometerILT1700 class needs an event to report it's received (and processed) data.
Your Form subscribes to that event
The Forms eventhandler uses this.Invoke() to overcome the cross-threading issue.
Invoke is a method on a Delegate or a Form or Control, since csRadiometerILT1700 is none of these it is not inheriting an Invoke implemenation from those classes.
You will need to raise another event to the caller of csRadiometerILT1700 and hadle that on you GUI somewhere (along with any cross thread issues.) Alternatively, you could provide csRadiometerILT1700 with a delegate it could use to callback, kind of like a hand rolled event.
Once you have the data in your Form you can use Control.InokeRequired to detect a cross thread situation and Control.Invoke to make the cross thread call.
anytime you try to post to the main thread from a different thread you'll get this error. You need to use a delegate and Invoke on the control you want to call from the thread that isn't the main form thread.
Assuming the error is actually:
Control control name accessed from a thread other than the thread it
was created on.
The problem is you can't affect your GUI from another thread. You need to invoke the form or control to pass control so it can be modified. You should be able to interact with most other elements, but not a form.
You should not call this.Invoke there, the way you are doing it is like trying to sync your own thread with your own instead with the UI. you should call formhandle.Invoke, or inside form class register for the event and then you can use this.invoke