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.
Related
I currently have an installation "framework" that does specific things. What I need now to do is be able to call my form in parallel with my script. Something like this:
InstallationForm f = new InstallationForm();
Application.Run(f);
InstallSoftware(f);
private static void InstallSoftware(InstallationForm f) {
f.WriteToTextbox("Starting installation...");
Utils.Execute(#"C:\temp\setup.msi", #"-s C:\temp\instructions.xml");
...
f.WriteToTextbox("Installation finished");
The current way I can do this is by adding the Form.Shown handler in InstallSoftware, but that seems really messy. Is there anyway I can do this better?
Your code will not work, because Application.Run(f) returns not until the form was closed.
You may use a simplified Model/View/Controller pattern. Create an InstallationFormController class that has several events, e.g. for textual notifications to be written to your textbox. The InstallationForm registers on these events in it's OnLoad() method and then calls InstallationFormController.Initialize(). That method starts your installation (on a worker thread/task). That installation callback method fires several text events.
InstallationForm f = new InstallationForm(new InstallationFormController());
Application.Run(f);
internal class InstallationFormController
{
public event EventHandler<DataEventArgsT<string>> NotificationTextChanged;
public InstallationFormController()
{
}
public void Initialize()
{
Task.Factory.StartNew(DoInstallation);
}
private void DoInstallation()
{
...
OnNotificationTextChanged(new DataEventArgsT<string>("Installation finished"));
}
private void OnNotificationTextChanged(DataEventArgsT<string> e)
{
if(NotificationTextChanged != null)
NotificationTextChanged(this, e);
}
}
public class DataEventArgsT<T> : EventArgs
{
...
public T Data { get; set; }
}
internal class InstallationForm : Form
{
private readonly InstallationFormController _controller;
public InstallationForm()
{
InitializeComponent();
}
public InstallationForm(InstallationFormController controller) : this()
{
if(controller == null)
throw new ArgumentNullException("controller")
_controller = controller;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_controller.NotificationTextChanged += Controller_NotificationTextChanged;
_controller.Initialize();
}
protected virtual void Controller_NotificationTextChanged(object sender, DataEventArgsT<string> e)
{
if(this.InvokeRequired)
{ // call this method on UI thread!!!
var callback = new EventHandler<DataEventArgsT<string>>(Controller_NotificationTextChanged);
this.Invoke(callback, new object[] {sender, e});
}
else
{
_myTextBox.Text = e.Data;
}
}
...
}
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);
}
I have 3 classes namely Login, Barcode, and the Main.
Login class just contains the authentication of the users.
Barcode class has the following snippet code:
class Barcode
{
public delegate void BarcodeReadHandler(object sender, BarcodeEventArgs e);
public event BarcodeReadHandler BarcodeReadOut;
public Barcode()
{
//.. some codes for getting data on the scanner
BarcodeEventArgs args = new BarcodeEventArgs(scannedData);
BarcodeReadOut(this, args);
}
}
While in Main class, the subsciption of the Barcode event is done:
public partial class Main : Form
{
private Barcode barcode = null;
public Main()
{
barcode.BarcodeReadOut += new barcode.BarcodeReadHandler(getBarcodeStr);
}
//This is called before log-out.
public void removeInstance()
{
barcode.BarcodeReadOut -= new barcode.BarcodeReadHandler(getBarcodeStr);
}
private void getBarcodeStr(object sender, BarcodeEventArgs e)
{
//some code
}
}
The duplication of event subscription happens when I try to logout and login again.
When I tried to debug, BarcodeReadOut is called twice.
In logout, the removeInstance() is called and the Main form is Close() and Dispose() before opening the login screen.
Can someone help me on how can I avoid the duplication of the said events?
I also have done this before registering the event but nothing happens:
public Main()
{
barcode.BarcodeReadOut -= new barcode.BarcodeReadHandler(getBarcodeStr);
barcode.BarcodeReadOut += new barcode.BarcodeReadHandler(getBarcodeStr);
}
You should add and remove the handler as follows:
public partial class Main : Form
{
private Barcode barcode = null;
public Main()
{
barcode.BarcodeReadOut += getBarcodeStr;
}
//This is called before log-out.
public void removeInstance()
{
barcode.BarcodeReadOut -= getBarcodeStr;
}
private void getBarcodeStr(object sender, BarcodeEventArgs e)
{
//some code
}
}
Also: You don't need to define a custom delegate, you can use the generic EventHandler:
public event EventHandler<BarcodeEventArgs> BarcodeReadOut;
It would be good to move all your logic that works with Barcode to a separate class. And it might be good to add a custom event that notifies other classes (a Form class in your case) that event has occurred :
class Barcode
{
public delegate void BarcodeReadHandler(object sender, BarcodeEventArgs e);
public event BarcodeReadHandler BarcodeReadOut;
public Barcode()
{
//.. some codes for getting data on the scanner
BarcodeEventArgs args = new BarcodeEventArgs(scannedData);
BarcodeReadOut(this, args);
}
}
class BarcodeWorker
{
private Barcode barcode = null;
private BarcodeReadHandler handler;
public event BarcodeEventArgs scanComplete;
BarcodeWorker(Barcode barcode)
{
if(barcode == null) this.barcode = barcode;
}
public AddEventHandler()
{
if(handler != null) return;
handler = new BarcodeReadHandler(getBarcodeStr);
barcode.BarcodeReadOut += handler;
}
//This is called before log-out.
public void RemoveEventHandler()
{
barcode.BarcodeReadOut -= handler;
handler = null;
}
private void getBarcodeStr(object sender, BarcodeEventArgs e)
{
scanComplete(sender, e);
}
}
And use it like this:
BarcodeWorker barcode = new BarcodeWorker();
barcode.scanComplete += // your delegate with event handler or with anonymous method here;
I have the following code:
namespace SSS.RemoteTruckService
{
public partial class Startup : Form
{
private Timer _gpsTimer;
private Timer _ppsTimer;
private Timer _creditCardTimer;
private Timer _iniTimer;
public string Message
{
get { return richTextBox_Message.Text; }
set
{
richTextBox_Message.Invoke((MethodInvoker)(()
=> richTextBox_Message.Text = DateTime.Now + " " +
value + Environment.NewLine + richTextBox_Message.Text));
}
}
public Startup()
{
InitializeComponent();
}
private void ButtonStartClick(object sender, EventArgs e)
{
StartRemoteTruck();
}
private void ButtonPauseClick(object sender, EventArgs e)
{
if (_gpsTimer.Enabled) _gpsTimer.Enabled = false;
if (_ppsTimer.Enabled) _ppsTimer.Enabled = false;
if (_creditCardTimer.Enabled) _creditCardTimer.Enabled = false;
if (_iniTimer.Enabled) _iniTimer.Enabled = false;
ProcessIniFile.StopProcess();
}
public void StartRemoteTruck()
{
Message = "RemoteTruck started.";
if (Settings.GlobalSettings == null)
{
Message = "GlobalSettings was null or not loaded. Cannot continue.";
Logging.Log("GlobalSettings was null or not loaded. Cannot continue.", "RemoteTruck", Apps.RemoteTruckService);
Environment.Exit(0);
}
if (Settings.GlobalSettings.IniFileWatcherEnabled)
{
ProcessIniFile.StartProcess();
}
CreateTimers();
}
And in the ProcessIniFile.StartProcess() I have the code:
namespace SSS.RemoteTruckService.inifile
{
public static class ProcessIniFile
{
private static DateTime _iniLastWriteTime;
private static readonly string Inifile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "sss.ini");
private static FileSystemWatcher _watcher;
public static void StartProcess()
{
ReadIniFile();
SaveCurrentIniReadings();
CreateIniFileWatcher();
}
public static void StopProcess()
{
if (_watcher != null)
{
_watcher.EnableRaisingEvents = false;
_watcher = null;
}
}
private static void CreateIniFileWatcher()
{
_watcher = new FileSystemWatcher
{
Path = Environment.GetFolderPath(Environment.SpecialFolder.Windows),
NotifyFilter = NotifyFilters.LastWrite,
Filter = "sss.ini"
};
_watcher.Changed += SssIniWatcherChanged;
_watcher.EnableRaisingEvents = true;
}
I'd like to pass back the the calling form the status of the reads of the file watcher.
Maybe I'm overthinking this, but if I want to add to the Message on the main form, how do I get to it?
You can use Events for that. Your process can send events and your form can handle them.
More info: http://msdn.microsoft.com/en-us/library/awbftdfh.aspx
The simple but not pretty way I like to use is to make that part of the form static as well. For example, creating a static variable WriteMessage, and in your Form Load or Startup(), you can set it:
WriteMessage = (s) => Message = s;
Sure this has some issues, but it's a quick way to get it done. One of those issues is that, you may need to use Dispatcher.invoke if you're not on the UI thread.
I want to rebuild the "look & feel" of MessageBox.Show() of WinForms applications.
With "look & feel" I mean to JUST CALL the MessageBox.Show() command with the logic of my already implemented ShowMessage method.
My current codebehind:
public partial class Testpage : System.Web.UI.Page
{
public enum MessageBoxIcon { Information, Error, Warning }
protected void Page_Load(object sender, EventArgs e)
{
ShowMessage("Testmessage", MessageBoxIcon.Error, ex);
}
private void ShowMessage(string title, string message, MessageBoxIcon type, Exception ex)
{
if (type == MessageBoxIcon.Information)
{
pnlMsgBox.CssClass = "MessageBoxInformation";
imgMsgBox.ImageUrl = "Resources/img/icon/MessageBoxIcon.Information.png";
}
else if (type == MessageBoxIcon.Warning)
{
pnlMsgBox.CssClass = "MessageBoxWarning";
imgMsgBox.ImageUrl = "Resources/img/icon/MessageBoxIcon.Warning.png";
}
else if (type == MessageBoxIcon.Error)
{
pnlMsgBox.CssClass = "MessageBoxError";
imgMsgBox.ImageUrl = "Resources/img/icon/MessageBoxIcon.Error.png";
}
else
throw new NotImplementedException();
lblMsgBox.Text = "<span style=\"font-size: large;\">" + title + "</span><br /><br />" + message;
#if (DEBUG)
if (ex != null)
lblMsgBox.Text += "<br /><br /><b>Debug information:</b> " + ex.Message;
#endif
pnlMsgBox.Visible = true;
updPnl.Update();
}
}
I'd like to call something like this:
protected void Page_Load(object sender, EventArgs e)
{
MessageBox.Show("Testmessage", MessageBoxIcon.Error, ex);
}
But
public static class MessageBox : Testpage
{
public static void Show(string message)
{
// do stuff with the page controls.
}
}
doesn't work as you all know, because Testpage isn't static.
Does anybody have some suggestions for me?
Reading the comments to the previous answer, I think next:
Its difficult to get access to current page context from the separated static class. So then, i recomendate do next:
Define next interface:
public interface IMessageBoxContainer
{
UpdatePanel UpdatePanel { get; }
Label Label { get; }
}
and implement him by your page as:
public partial class _Default : System.Web.UI.Page, IMessageBoxContainer
{
protected void Page_Load(object sender, EventArgs e)
{
}
public UpdatePanel UpdatePanel
{
get { return updatePanel1; }
}
public Label Label
{
get { return label1; }
}
}
This steps provide you to get access to your page from anywhere. After that implement you MessageBox NON-static class:
public class MessageBox
{
private IMessageBoxContainer container = null;
public MessageBox(IMessageBoxContainer _container)
{
container = _container;
}
public void Show(string message)
{
container.Label.Text = message;
container.UpdatePanel.Update();
}
}
Why non-static? Becouse if you do container field as static and set him value anywhere in page (for example in Page_Load), when another page will load - your container property will reset. But without this field, that pointer to your current page, you cant get access to your specefied controls.
After that you can use your class like that:
protected void Button1_Click(object sender, EventArgs e)
{
var box = new MessageBox(this);
box.ShowMessage("hello world");
}
So, you get separated implementation logic of your MessageBox and simple using him, yes? But unfurtunently, its non-static.
Try like this:
public static class MessageBox
{
public static void Show(string message)
{
HttpContext.Current.Response.Write(string.Format("<script>alert('{0}');</script>", message));
}
}
Of course, you will need to implement your own correct JavaScript to display you MessageBox on client-side. But I thing – you approach is wrong. For you method programming try to look in ASP.NET MVC, becouse classic ASP.NET do not fully support JavaScript coding.