C# Windows Service Events - c#

So I am just messing around here nothing production just proof of concept with my first ever Windows Service.
I am trying to essentially create a windows service that sits as the listener for a signalr connection. In essence, I will have a windows application and a windows service. The win service will handle connecting to the signalr hub and on signalr calls fire an event. The windows application will listen for these events and perform actions based on them.
Currently I cannot get this to work. I have never worked with events, or windows services before. In my windows application my events never hit their break points, as well I log an error of null reference exception from the
ConnectToHub()
Alright if I comment out the OnConnected() method call I log a successful connection to the hub. I have never worked with events before so is my mistake with the events?
I debated that this approach was a bit overkill. However, for me it was a proof of concept that I could find a use for a long running windows service, and adding some events into the mix.
Code for service:
public delegate void MessageRecievedEventHanlder(object sender, MessageRecievedArgs e);
public delegate void ConnectedToHubEventHandler(object sender, ConnectedArgs e);
public partial class SignalRService : ServiceBase
{
IHubProxy _hub;
HubConnection connection;
string url = #"http://localhost:8080/";
private Message LastMessage;
public static event MessageRecievedEventHanlder NewMessage;
protected virtual void OnNewMessage(MessageRecievedArgs e)
{
NewMessage(null, e);
}
public static event ConnectedToHubEventHandler Connected;
protected virtual void OnConnected(ConnectedArgs e) {
System.IO.File.WriteAllText(#"C:\Users\Bailey Miller\Desktop\FTP\Logg.txt", "Hit OnConnected " + e.Success +" " + Connected != null ? "Isn't null" : "Null event");
Connected(null, e);
}
public SignalRService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
ConnectToHub().Wait();
}
private async Task ConnectToHub()
{
try
{
//Connecting
if (connection == null)
{
connection = new HubConnection(url);
}
if (_hub == null)
{
_hub = connection.CreateHubProxy("ChatHub");
}
await connection.Start();
//Connected
//Configure all the incoming options
_hub.On<Message>("RecieveMessage", IncomingMessage);
System.IO.File.WriteAllText(#"C:\Users\Bailey Miller\Desktop\FTP\Succes.txt", "Connected");
OnConnected(new ConnectedArgs(true));
}
catch (Exception ex)
{
//Failed
//OnConnected(new ConnectedArgs(false));
System.IO.File.WriteAllText(#"C:\Users\Bailey Miller\Desktop\FTP\Fail.txt", "Failed to connect " + ex.Message.ToString());
}
}
private void IncomingMessage(Message state)
{
DateTime? lmt;
//Determine if has lastmessagetime
if (LastMessage == null) {
lmt = null;
}
else {
lmt = LastMessage.RecievedAt;
}
LastMessage = state;
//New Message
//OnNewMessage(new MessageRecievedArgs(state, lmt));
}
protected override void OnStop()
{
}
}
public class MessageRecievedArgs : EventArgs
{
public Message NewMessage { get; }
public DateTime? LastMessageTime { get; }
public MessageRecievedArgs(Message msg, DateTime? lmt) {
this.NewMessage = msg;
this.LastMessageTime = lmt;
}
}
public class ConnectedArgs : EventArgs {
public bool Success { get; }
public ConnectedArgs(bool suc) {
this.Success = suc;
}
}
My windows application as of now:
public MainWindow()
{
InitializeComponent();
SignalRService.SignalRService.NewMessage += SignalRService_NewMessage;
SignalRService.SignalRService.Connected += SignalRService_Connected;
}
private void SignalRService_Connected(object sender, SignalRService.ConnectedArgs e)
{
throw new NotImplementedException();
}
private void SignalRService_NewMessage(object sender, SignalRService.MessageRecievedArgs e)
{
throw new NotImplementedException();
}

Your question is a bit broad- you don't describe exactly what isn't working, so I am guessing that when you start your service, it says "starting..." for a long while and eventually windows service manager gives you an error saying your service didn't start in a timely fashion. The issue is that OnStart() is expected to return- you can't block the thread there with the Wait() call. My suggestion would be to spawn a new background thread here to perform the waiting, then exit. That should get you past the first hurdle.
As another aside... You can add a regular main method to a windows service project, change the project type to Console Application, and run it that way to reduce your debugging cycle time. Then when you are sure it basically works, change the project type back to Windows Service and install it.
EDIT: Now that you have a better error description, I see the real problem. The issue is that you are raising an event without checking for null first. Event fields are null until you attach a listener. So change your code as follows:
protected virtual void OnConnected(ConnectedArgs e) {
System.IO.File.WriteAllText(#"C:\Users\Bailey Miller\Desktop\FTP\Logg.txt", "Hit OnConnected " + e.Success +" " + Connected != null ? "Isn't null" : "Null event");
ConnectedToHubEventHandler connectedEvent = Connected;
if (connectedEvent != null) // This event might be null, so check first
connectedEvent(null, e);
}

Related

Why am I getting a NullReferenceException when subscribing to event in Xamarin.Forms, but only on iOS?

I have a page - Page A, that has a method that subscribes to an event on another page - Page B. I figured I could instantiate Page B in my code in Page A before having my method in Page A subscribe to the event in Page B, and then finally pushing Page B to the navigation stack.
Unfortunately, I keep getting a NullReferenceException on the line in which the method subscribes to the event when I test my application on iOS. The code runs perfectly fine when I deploy and test as an Android application, but I always get the NullReferenceException on iOS. What's causing this exception to be thrown, and how can I fix it? Why is it platform specific to iOS?
Code on Page A
var confirmationPage = new EmailConfirmationPage(username);
confirmationPage.EmailConfirmed += this.OnEmailConfirmed;
await this.Navigation.PushModalAsync(confirmationPage);
...
private void OnEmailConfirmed(object source, EventArgs args)
{
this.LabelMessage.Text = "Email Confirmed!";
}
Code on Page B
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace appFBLA2019
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class EmailConfirmationPage : ContentPage
{
private string username;
private string email;
public delegate void EmailConfirmedEventHandler(object source, EventArgs args);
public event EmailConfirmedEventHandler EmailConfirmed;
public EmailConfirmationPage(string username)
{
InitializeComponent();
this.username = username;
this.LabelTitle.Text = "Loading...";
GetEmail();
}
private void GetEmail()
{
try
{
ServerConnector.QueryDB($"getEmail/{this.username}/-");
this.email = ServerConnector.ReceiveFromDB();
this.LabelTitle.Text = $"Enter the confirmation code sent to {this.email.Split('/')[1]}";
}
catch
{
this.LabelMessage.Text = "Connection Error: Please Try Again.";
}
}
private async void ButtonConfirmEmail_Clicked(object sender, EventArgs e)
{
try
{
string userInputToken = this.EntryConfirmationCode.Text.Trim();
ServerConnector.QueryDB($"confirmEmail/{this.username}/{userInputToken}/-");
string returnData = ServerConnector.ReceiveFromDB();
if (returnData == "true/-")
{
OnEmailConfirmed();
await this.Navigation.PopModalAsync(true);
}
else
{
this.LabelMessage.Text = "Email could not be confirmed. Please try your code again.";
}
}
catch
{
this.LabelMessage.Text = "Connection Error: Please try again.";
}
}
private void ButtonFixEmail_Clicked(object sender, EventArgs e)
{
string newEmail = this.EntryChangeEmail.Text;
ServerConnector.QueryDB($"changeEmail/{this.username}/{newEmail}/-");
string result = ServerConnector.ReceiveFromDB();
if (result == "true/-")
{
this.LabelMessage.Text = $"Enter the confirmation code sent to {newEmail}";
}
else
{
this.LabelMessage.Text = $"Email could not be changed: {result.Split('/')[1]}";
}
}
private async void ButtonClose_Clicked(object sender, EventArgs e)
{
await this.Navigation.PopModalAsync(true);
}
protected virtual void OnEmailConfirmed()
{
EmailConfirmed?.Invoke(this, EventArgs.Empty);
}
}
}
Call Stack before subscribing method to event:
0xC0 in appFBLA2019.CreateAccountPage.ButtonCreateAccount_Clicked at C:\Users\chung\source\repos\appFBLA2019\appFBLA2019\appFBLA2019\CreateAccountPage.xaml.cs:30,21 C#
Call stack after subscribing method to event:
0x1B8 in appFBLA2019.CreateAccountPage.ButtonCreateAccount_Clicked at C:\Users\chung\source\repos\appFBLA2019\appFBLA2019\appFBLA2019\CreateAccountPage.xaml.cs:39,13 C#
Upon further testing, I noticed that this issue occurs on both iOS and Android, but ONLY when running the application with Xamarin Live Player. I contacted Microsoft and they pointed out that Xamarin Live Player unfortunately has limitations. Deploying directly to a device causes no issues, and the code runs fine.

Event is sometimes null

I'm having an odd intermittent issue that I can't seem to reproduce consistently.
I have a custom NLog target called NotifyTarget, shown below:
internal delegate void NotifyTargetEventArgs(string message);
[Target("NotifyTarget")]
internal class NotifyTarget : TargetWithLayout
{
public event NotifyTargetEventArgs Notify;
private List<string> _logs = new List<string>();
public IList<string> Logs
{
get
{
return new ReadOnlyCollection<string>(_logs);
}
}
protected override void Write(LogEventInfo logEvent)
{
string message = Layout.Render(logEvent).TrimEnd();
_logs.Add(message);
OnNotify(message);
}
protected void OnNotify(string message)
{
if (Notify != null)
Notify(message);
}
}
I have an application with a child form called ErrorConsole that attaches to NotifyTarget.Notify:
public partial class ErrorConsole : Form
{
static NotifyTarget target;
static ErrorConsole()
{
target = LogManager.Configuration.FindTargetByName<NotifyTarget>("notify");
}
public ErrorConsole()
{
InitializeComponent();
}
private void ErrorConsole_Load(object sender, EventArgs e)
{
LoadLogs();
target.Notify += Log;
}
private void LoadLogs()
{
errorBox.AppendText(string.Join(Environment.NewLine + Environment.NewLine, target.Logs));
ScrollToLast();
}
private void Log(string message)
{
errorBox.Invoke((MethodInvoker)(() =>
{
string prefix = errorBox.Text == string.Empty ? "" : Environment.NewLine + Environment.NewLine;
errorBox.AppendText(prefix + message);
ScrollToLast();
}));
}
private void ScrollToLast()
{
errorBox.ScrollToCaret();
}
private void ErrorConsole_FormClosing(object sender, FormClosingEventArgs e)
{
target.Notify -= Log;
}
}
The ErrorConsole is loaded from a parent form (let's call it MainForm) after clicking a menu:
public partial class MainForm : Form
{
private ErrorConsole console;
// ...
private void errorConsoleMenu_Click(object sender, EventArgs e)
{
if (console == null || console.IsDisposed == true)
{
console = new ErrorConsole();
}
console.Show();
}
}
Whenever I do encounter this issue, I can set a breakpoint after target.Notify += Log; and inspect target.Notify with the debugger to verify that the Log method is indeed being registered:
However, if I put a breakpoint on if (Notify != null) and inspect Notify after continuing execution and triggering a log event, it appears to have been changed to null at some point:
When the application enters this "bad state", it persists for the whole session -- closing the form and trying again continues to fail, but restarting the application usually puts it back into a "good state".
What could be the cause of this behaviour?
You might have a rogue conditional breakpoint.
If you have one where you're testing for null, but the condition is
Notify = null
instead of
Notify == null
Then the breakpoint would be setting Notify to null rather than evaluating its value.

Why this MessageBox does not appear?

I Have Code for EventHandler like this below.
void ConnectionManager_Error(object sender, EventArgs<string> e)
{
BeginInvoke((MethodInvoker)delegate()
{
State = ConnectState.NotFound;
MessageBox.Show(e.Value);
});
}
My Problem:
MessageBox never appears even when the device is not connected to the computer.
I think that comes MessageBox supposed that show error messages.
Can someone show me what is wrong?
Note:
I have this code that I thought would trigger ConnectionManager Error EventHandler.
private void LogError(string error)
{
if (Error != null)
Error(this, new EventArgs<string>(error));
}
I also have this code that gives an error message containing the string to LogError method.
int lasterror = Marshal.GetLastWin32Error();
if (lasterror != 0)
LogError("Bluetooth API returned: " + lasterror.ToString());
or
if (BluetoothSetServiceState(IntPtr.Zero, ref device, ref HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE) != 0)
LogError("Failed to connect to wiimote controller");
Another Hint
To be more specific, I also already have the code below:
public event EventHandler<EventArgs<string>> Error;
and
ConnectionManager.Error += new EventHandler<EventArgs<string>>(ConnectionManager_Error);
And also this class:
public class EventArgs<T> : EventArgs
{
public T Value
{
get;
set;
}
public EventArgs(T value)
: base()
{
Value = value;
}
}
Is it standard BeginInvoke that executes code in the separate thread?
You have to work with GUI methods only in GUI thread.
To switch to the GUI thread please try Control.IsInvokeRequired and Control.Invoke methods.

Handling events after receiving a MSMQ message (thread issue?)

I created two separate Windows Forms applications in C# that use MSMQ for communicating. Here's how it works, it looked simple enough though:
App1 sends a details request to App2.
App2 creates an event to open the window.
App2 opens a "details" window.
The only problem I have is that when received the message, the "details" window freezes after appearing.
As I handle MSMQ messages handling in an object that uses threads, I suspect the problem comes from there... But I have no experience in handling MSMQ messages or specific events handling between parts of an application.
Here's part of the code I use for App2:
/*Class declared in the Core namespace*/
public class TaskMessageQueueHandler
{
public TaskMessageQueueHandler()
{
this.Start();
}
private Thread m_thread;
private ManualResetEvent m_signal;
public event System.EventHandler messageReceived;
public void Start()
{
m_signal = new ManualResetEvent(false);
m_thread = new Thread(MSMQReceiveLoop);
m_thread.Start();
}
public void Stop()
{
m_signal.Set();
}
protected virtual void SendEvent(object sender, EventArgs e)
{
if (messageReceived != null)
messageReceived(this.message, e);
}
public string message;
private void MSMQReceiveLoop()
{
bool running = true;
MessageQueue queue = new MessageQueue(#".\Private$\queue1");
while (running)
{
try
{
var message = queue.Receive();
message.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
this.message = message.Body.ToString();
string m = this.message;
SendEvent(m, System.EventArgs.Empty);
if (m_signal.WaitOne(10))
{
running = false;
}
}
catch
{
Console.WriteLine("ERROR");
running = false;
}
}
}
}
/*Main process, in the Program namespace*/
[...]
Core.TaskMessageQueueHandler tmqh = new Core.TaskMessageQueueHandler();
EventListener el = new EventListener();
tmqh.messageReceived += new System.EventHandler(el.ShowDetails);
[...]
/* Class in the Program namespace */
class EventListener
{
public void ShowDetails(object sender, EventArgs e)
{
int numero = int.Parse(sender as string);
Details details = new Details(numero);
details.Show();
}
}
Where did I go wrong? Where did I go right?
Thanks a lot,
Stephane.P
EDIT: if the MSMQ handler is stopped with Stop() anywhere around the event sending, the details window appears then disappears right away...
EDIT2: After the workaround given by Slugart, I managed to make this work:
class EventListener
{
Main control;
public EventListener(Main main)
{
control = main;
}
public void ShowDetails(object sender, EventArgs e)
{
int numero = int.Parse(sender as string);
control.Invoke((Action)(() => ShowDetails(numero)));
}
private void ShowDetails(int numero)
{
Details details = new Details(numero);
details.Show();
}
}
Which is used like:
Core.TaskMessageQueueHandler tmqh = new Core.TaskMessageQueueHandler();
EventListener el = new EventListener(this);
tmqh.messageReceived += new System.EventHandler(el.ShowDetails);
You're creating and displaying a form Details on a thread other than the main GUI thread and not an STA thread at that.
Your EventListener should have a reference to a running form (your main form perhaps) and then call form.Invoke() on it.
class EventListener
{
Control control; // A valid running winforms control/form created on an STA thread.
public void ShowDetails(object sender, string message)
{
int numero = int.Parse(message);
control.Invoke(() => ShowDetails(numero))
}
private void ShowDetails(int numero)
{
Details details = new Details(numero);
details.Show();
}
}
Also sending your event data as the sender is not really following the Event pattern that has been put in front of you. You want to use the EventArgs parameter for this, use the EventHandler delegate (EventHandler in your case).

WCF NamedPipes Service and WPF Client

I'm working with a windows service where i want to add a GUI too it. I made a proof of concept with my service creating a ServiceHost object and hosting the WCF Named pipes service and then i Console application to consume the WCF service and also getting callback responses from the service (messages sent from server to connected clients). This works great my console application runs and get responses from the service with no interruption or delays.
However when doing the same thing in my WPF GUI application when clicking a button that then calls the WCF service it freezes the whole UI thread and then after a couple of minutes throws an exception and then the UI is updated with the message callback (server sends message to connected clients) but any return values from service is lost since the exception was thrown.
The two exception messages i have gothen is theses (the most common is the first):
1: The requesting action sent to net.pipe :/ / localhost / PipeGUI did not receive a response within the specified timeout (00:00:59.9989999). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Raise the deadline for action (by entering the channel / proxy to Icon Text Channel and set the property Operation Timeout) and verify that the service can connect to the client.
2: Communication object System.ServiceModel.Channels.ServiceChannel, can not be used for communication because it has been canceled.
Anyone got any ideas why this is happeing ?
I can post more code if neccessary.
UPDATE , added code for reference
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void MessageRecived(string message);
}
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))]
public interface IPipeServiceContract
{
[OperationContract]
string Hello();
[OperationContract]
void Message(string msg);
[OperationContract(IsInitiating = true)]
void Connect();
[OperationContract(IsTerminating = true)]
void Disconnect();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, UseSynchronizationContext = false)]
public class PipeService : IPipeServiceContract
{
List<IClientCallback> _clients = new List<IClientCallback>();
public string Hello()
{
PublishMessage("Hello World.");
return "Return from method!";
}
public void Connect()
{
_clients.Add(OperationContext.Current.GetCallbackChannel<IClientCallback>());
}
public void Disconnect()
{
IClientCallback callback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
_clients.Remove(callback);
}
void PublishMessage(string message)
{
for (int i = _clients.Count - 1; i > 0; i--)
{
try
{
_clients[i].MessageRecived(message);
}
catch (CommunicationObjectAbortedException coae)
{
_clients.RemoveAt(i);
}
catch(CommunicationObjectFaultedException cofe)
{
_clients.RemoveAt(i);
}
}
}
public void Message(string msg)
{
PublishMessage(msg);
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged, IClientCallback
{
public ServiceController Service { get; set; }
protected IPipeServiceContract Proxy { get; set; }
protected DuplexChannelFactory<IPipeServiceContract> PipeFactory { get; set; }
public ObservableCollection<ServerActivityNotification> Activity { get; set; }
public override void BeginInit()
{
base.BeginInit();
PipeFactory = new DuplexChannelFactory<IPipeServiceContract>(this, new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/PipeGUI"));
}
public MainWindow()
{
InitializeComponent();
Activity = new ObservableCollection<ServerActivityNotification>();
Service = ServiceController.GetServices().First(x => x.ServiceName == "Server Service");
NotifyPropertyChanged("Service");
var timer = new DispatcherTimer();
timer.Tick += new EventHandler(OnUpdate);
timer.Interval = new TimeSpan(0, 0, 0, 0, 850);
timer.Start();
if (Service.Status == ServiceControllerStatus.Running)
{
Proxy = PipeFactory.CreateChannel();
Proxy.Connect();
}
}
void OnUpdate(object sender, EventArgs e)
{
Service.Refresh();
NotifyPropertyChanged("Service");
StartButton.IsEnabled = Service.Status != ServiceControllerStatus.Running ? true : false;
StopButton.IsEnabled = Service.Status != ServiceControllerStatus.Stopped ? true : false;
if (PipeFactory != null && Service.Status == ServiceControllerStatus.Running)
{
Proxy = PipeFactory.CreateChannel();
Proxy.Connect();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private void OnStart(object sender, RoutedEventArgs e)
{
try
{
Service.Start();
}
catch
{
Service.Refresh();
}
}
private void OnStop(object sender, RoutedEventArgs e)
{
try
{
if (Proxy != null)
{
Proxy.Disconnect();
PipeFactory.Close();
}
Service.Stop();
}
catch
{
Service.Refresh();
}
}
public void MessageRecived(string message)
{
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
ServerActivityNotification log = new ServerActivityNotification { Activity = message, Occured = DateTime.Now };
Activity.Add(log);
ListBoxLog.ScrollIntoView(log);
NotifyPropertyChanged("Activity");
}));
}
private void OnHello(object sender, RoutedEventArgs e)
{
try
{
Proxy.Message(txtSendMessage.Text);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
}
Try setting the UseSynchronizationContext property of the service behaviour to false:
[ServiceBehavior(UseSynchronizationContext = false)]
class MyService
{
}
[ServiceContract]
public interface IMyService
{
}
I believe that by default this is set to true, so you are currently attempting to consume and run the WCF service on the same thread resulting in a deadlock.
In any case, it sounds like you are trying to consume the WCF service on the UI thread of the WPF application. Generally it is recommended that you perform potentially long running tasks on the background thread as this keeps the interface responsive even if your service call takes a few seconds/minutes.
EDIT:
I tried and succeeded in replicating your problem. Trying to call the service on the UI thread results in the UI freezing. However, when I changed my code to call the service on a background task (see below), I was able to call the service and receive the callback:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
var myService = DuplexChannelFactory<IMyService>.CreateChannel(new CallbackImplementation(),
new WSDualHttpBinding(),
new EndpointAddress(
#"http://localhost:4653/myservice"));
myService.CallService();
string s = "";
});
}
I have to confess, I am not entirely sure why this is so, and any clarification on exactly how WCF manages the thread hosting the service instance would be great in working out why this works.

Categories

Resources