Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would an event be null? (object reference not set to an instance of an object)

I have a form with a button, a label and a progress bar, so that when I click the button it creates an instance of class b to run a process. Once the process is done it will call an EventHandler to show "done" in the main form's label!

I created an event (SetStatusEvent) of a delegate (SetStatus) to do this. And it seems fine when I call this event outside the EventHandler (usbforProcessExited) but when I call it from usbforProcessExited it gives an error -

object reference not set to an instance of an object

main form

public partial class main : Form
{
    b rsSet = new b();

    public main()
    {
        InitializeComponent();
        rsSet.SetStatusEvent += new RemoteS.SetStatus(updateStatus);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        rsSet.FormatUSB();
    }

    private delegate void UpdateStatus(int i, string str, Color clr);

    private void SetStatus(int i, string str, Color clr)
    {
        this.progressBar1.Value = i;
        this.lbl_status.ForeColor = clr;
        this.lbl_status.Text = str;
    }

    private void updateStatus(int i, String msg, Color color)
    {
        object[] p = GetInokerPara(i, msg, color);
        BeginInvoke(new UpdateStatus(SetStatus), p);
    }

    private object[] GetInokerPara(int progress, string msg, Color color)
    {
        object[] para = new object[3];
        para[0] = progress;
        para[1] = msg;
        para[2] = color;

        return para;
    }
}

class b

class b
{
    public delegate void SetStatus(int i, string msg, Color color);
    public event SetStatus SetStatusEvent;

    System.Diagnostics.Process usbfor = new System.Diagnostics.Process();

    public void FormatUSB()
    {

        usbfor.StartInfo.FileName = @"usbformat.bat";
        usbfor.EnableRaisingEvents = true;
        usbfor.Exited += new EventHandler(usbforProcessExited);
        usbfor.Start();
    }

    public void usbforProcessExited(object sender, EventArgs f)
    {
        SetStatusEvent(100, "DONE", Color.Green); //ERROR HERE! (object reference not set to an instance of an object
    }
}

Where is the problem?

like image 437
daygoor Avatar asked Dec 03 '22 22:12

daygoor


2 Answers

The event is null, if there are no subscribers.

There are two solutions:

  1. Initialize the event when declaring (dummy subscriber doing nothing):

    public event SetStatus SetStatusEvent = delegate { };
    
  2. Check the event for null before raising (in a thread-safe way):

    public void usbforProcessExited(object sender, EventArgs f)
    {
        SetStatus setStatus = SetStatusEvent;
        if (setStatus != null)
        {
            setStatus(100, "DONE", Color.Green);
        }
    }
    
like image 104
DavWEB Avatar answered Dec 28 '22 07:12

DavWEB


You have a race condition:

usbforProcessExited gets subscribed in the constructor of b and might be invoked before you called rsSet.SetStatusEvent += new RemoteS.SetStatus(updateStatus).

You should only call usbfor.Start() after you subscribed to SetStatusEvent.

A related problem is that the event will run on another thread. You should set rsSet.SynchronizingObject before starting the process so your event handler can modify the form without manually calling Invoke/BeginInvoke.

like image 45
CodesInChaos Avatar answered Dec 28 '22 08:12

CodesInChaos