-1

I invoke a "Find" form from FrmDelivery. The Find form is crashing on attempting to scan a barcode into an editbox (the same code works fine elsewhere in the app, such as in FrmDelivery).

The log file shows that FrmDelivery is the form experiencing an exception:

Message: Reached frmDelivery.StartRead

Date: 2/19/2015 7:39:39 PM
Message: From FrmDelivery.StartRead(): The scanner not enabled, Call Enable() first.; Inner Ex: ; Stack Trace:    at 
Symbol.Barcode.Actions.Read(ReaderData rd)

Yet, prior to opening the Find form, I close FrmDelivery ("this" below):

private void buttonFind_Click(object sender, EventArgs e)
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmDelivery.buttonFind_Click");
    this.Close(); // Close the Delivery form; this still leaves frmMain up
    const HHSConsts.RecordTypes rt = HHSConsts.RecordTypes.Delivery;
    frmFind ff = new frmFind(rt, dsdName);
    ff.ShowDialog();
}

StartRead() is the method throwing the exception:

private void StartRead()
{
    ExceptionLoggingService.Instance.WriteLog("Reached 
frmDelivery.StartRead");
    try
    {
        // If we have both a reader and a reader data
        if ((this.barcodeReader != null) && (this.barcodeReaderData != 
null)) 
        {
            if (this.barcodeReaderData.IsPending) return;
            // Submit a read
            this.barcodeReader.ReadNotify += this.barcodeEventHandler;
            this.barcodeReader.Actions.Read(this.barcodeReaderData);
        }
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format(
                "{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, 
ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From 
FrmDelivery.StartRead(): {0}", msgInnerExAndStackTrace));
    }
}

// StartRead() is called four places in FrmDelivery:

private void textBoxUPC_PLU_GotFocus(object sender, EventArgs e)
{
    textBoxUPC_PLU.BackColor = HHSConsts.BARSCAN_COLOR; // Why is this
not sticking?
    ExceptionLoggingService.Instance.WriteLog("Reached
frmDelivery.textBoxUPC_PLU_GotFocus");
    if (this.InitReader())
    {
        this.StartRead();
    }
}

private void BarcodeReader_ReadNotify(object sender, EventArgs e)
{
    ExceptionLoggingService.Instance.WriteLog("Reached
frmDelivery.BarcodeReader_ReadNotify");
    try
    {
        Symbol.Barcode.ReaderData TheReaderData =
this.barcodeReader.GetNextReaderData();

        if (TheReaderData.Result == Symbol.Results.SUCCESS)
        {
            // Handle the data from this read
            this.HandleData(TheReaderData);
            // Start the next read
            this.StartRead();
        }
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format(
                "{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message,
ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From
FrmDelivery.BarcodeReader_ReadNotify(): {0}",
msgInnerExAndStackTrace));
    }
}

private void ReaderForm_Activated(object sender, EventArgs e)
{
    ExceptionLoggingService.Instance.WriteLog("Reached
frmDelivery.ReaderForm_Activated");
    // If there are no reads pending on barcodeReader, start a new read
    if (!this.barcodeReaderData.IsPending)
    {
        this.StartRead();
    }
}

private void textBoxId_GotFocus(object sender, EventArgs e)
{
    ExceptionLoggingService.Instance.WriteLog("Reached
frmDelivery.textBoxId_GotFocus");
    if (this.InitReader())
    {
        this.StartRead();
    }
}

But how could any of these methods call StartRead() after the form has been closed?

Jørgen R
  • 10,568
  • 7
  • 42
  • 59
B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
  • 1
    I'm confused: you say the Find form is crashing, but the log excerpt seems to say that frmDelivery is the source, which is also said to be the form closed in the first code block. At any rate is frmDelivery a dialog too? – Ňɏssa Pøngjǣrdenlarp Feb 20 '15 at 02:06
  • Yes, that's my question - why are a closed form's events firing? FrmDelivery is invoked from frmMain via ShowDialog(); frmFind is invoked from FrmDelivery using ShowDialog(). – B. Clay Shannon-B. Crow Raven Feb 20 '15 at 05:38
  • 2
    Side Note: `this.barcodeReader.ReadNotify += this.barcodeEventHandler;` looks like trouble because it appears to keep adding this event handler multiple times. That could make the method run multiple times. – LarsTech Feb 20 '15 at 16:58
  • @LarsTech: So would that code be better moved to the constructor, or first unhook the handler prior to hooking it up (-=)? – B. Clay Shannon-B. Crow Raven Feb 20 '15 at 17:02

1 Answers1

1

…how could any of these methods call StartRead() after the form has been closed?

That depends. Is the barcode reader object still around? I.e. is it referenced by anything?

Closing a form does one of two things, depending on if it's modal or not: hides the window (for modal windows), or closes and disposes the window (for non-modal windows). That's pretty much all you're guaranteed of (not counting other logic you add in e.g. a FormClosed event handler).

In particular, closing a form does not in any way in and of itself cause that object to be garbage-collected or otherwise disabled. Many forms are referenced only by the Winforms framework and when closed, those references are discarded. But if you do anything to cause your form to be referenced elsewhere, it will live on.

Your FrmDelivery form appears to have subscribed to the barcode reader object's ReadNotify event. This involves adding a delegate instance to the invocation list for the event, and since the event handler method is an instance method, that delegate instance includes a reference to the FrmDelivery object.

As long as the barcode reader object is still reachable (or if it's a static class, and hence all of its members are always reachable), that means your FrmDelivery object is also still reachable. That is, the barcode reader object retains the delegate object which in turn references the FrmDelivery object.

Since your event handler method is in fact one of the ways that your StartRead() method could be called, it seems very likely that this is in fact what's going on. There's not enough context in your code example for anyone reading it to be absolutely sure, but the probability is very high. It'd be a good bet. :)


By the way, besides the event handler subscription (which from the problem description looks like it would keep the FrmDelivery object alive anyway), you seem to be nesting your ShowDialog() calls in a way that also would prevent the FrmDelivery object from being collected.

Specifically: you are calling Close() on the FrmDelivery object in the event handler for a button on the form. That handler does not return until the ShowDialog() method call in that handler returns, and until the handler returns, your original ShowDialog() call for the FrmDelivery window can't return, and until that ShowDialog() call returns, the caller of that method can't dispose of or otherwise discard the form instance.

This has nothing to do with whether the event handler itself is called, but it does have an effect on the lifetime of the object.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136