Search code examples
c#debuggingcompact-frameworkwindows-cestreamwriter

Why would my text file suddenly stop being saved?


Mildly chastised here by wiser heads, I switched my calls to MessageBox.Show() to writing lines to a StringBuilder which is later saved to a file, both from the app's custom exception handler:

public static void ExceptionHandler(Exception ex, string location)
{
    try
    {
        if (inDebugMode)
        {
            LogMsgs.Append(string.Format("{0}\r\n", ex.Message)); 
            DateTime dt = DateTime.Now;
            string timeAsStr = string.Format("{0}_{1}_{2}_{3}.txt", dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
            using (StreamWriter file = new StreamWriter(timeAsStr))
            {
                // If the app crashes, this is how the file is written; if it doesn't, it's written
                // in frmCentral.Form1_Closing()
                file.WriteLine(LogMsgs.ToString());
            }
        }
        MessageBox.Show("Exception: " + ex.Message + "\n\nLocation: " + location, GetFormTitle("CCR: " + ex.GetType().FullName,"",""));
    }

(...which is called like this:

catch (Exception ex )
{
    CCR.ExceptionHandler(ex, "WriteXML.WriteFile");
    . . .

) ...and from the main form's Closed() event:

private void frmCentral_Closed(object sender, EventArgs e)
{
    if (CCR.inDebugMode)
    {
        DateTime dt = DateTime.Now;
        string timeAsStr = string.Format("{0}_{1}_{2}_{3}.txt", dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
        using (StreamWriter file = new StreamWriter(timeAsStr))
        {
            // If the app closes normally, this is how the file is written; if it doesn't, 
            // (it crashed) it's written in PDAClient.ExceptionHandler()
            file.WriteLine(CCR.LogMsgs.ToString());
        }
    }
} // frmCentral_Closed

This worked just dandy until earlier today. I made no change to that code. But now, whether the code crashes or not (I am able to close it from the main form), the file is not being written (at least not to where it had been written before, namely to the root of the handheld device).

I'm adding suchlike messages:

CCR.LogMsgs.Append(string.Format("DBCommand exception: {0}\r\n", ex.Message));

..and the inDebugMode boolean is indeed being set to true and nowhere to false:

public static bool inDebugMode = true; //TODO: Change this to false before deploying.

What could be causing this malevolent omission?

UPDATE

To answer jp2code:

This is what ExceptionHandler does in its catch block:

catch(Exception exc)
{
    MessageBox.Show("Exception Handler generated an exception!\n" + exc.Message + "\n\nCalling Location: " + location, GetFormTitle("CCR: " + exc.GetType().FullName,"",""));
}

Note that I didn't write this code and thus I often (usually?) don't know the motivation or mindset behind much of it. It seems spaghettier than an Italienfest to me, but maybe I'm just not grokking it.

Many things are not what they appear in the code, e.g., void functions which are named as if they are bool functions, and this ExceptionHandler() method which you would think (I would, anyway), that it is a global/catch-all exception handler, but really it has to be explicitly called from catch blocks throughout the code to execute.

Also: I tried to implement the errHndlrLock code, but get compile err msg, "An object reference is required for the nonstatic field, method, or property 'PDAClient.SSCS.errHndlrLock'"

UPDATE 2

Today it's back to working; must have been a memory issue or something similar - although I did perform a warm boot yesterday with no respite. So: it worked, it suddently failed to work, and just as suddenly (overnight), it began to work again.

UPDATE 3

I think this is a clue to what happened in the case of the incredibly mysteriously disappearing debug log text file. When I opened up the most recent ones today, they are chock full of:

StackOverflowException
StackOverflowException
StackOverflowException
StackOverflowException
StackOverflowException
StackOverflowException
StackOverflowException

...enough of those, and the whole shebang decided to take a vacation, I reckon. Now, though, it is again on hiatus - this CE jazz is flakier than my grandmother's pie crust.


Solution

  • Nat could be onto something with that Flush call. I gave him a +1 on that.

    Another thing that could be happening (that I ran into) is multiple items trying to access your Exception Handler at one time.

    Say you are closing your form and there is an Exception being caught in one of your methods. Now you have two (2) routines trying to access your timeAsStr file at one time, which can't happen.

    A solution to that would be to add a lock to your Exception Handler routine:

    private object errHndlrLock = new Object();
    
    public void ExceptionHandler(Exception ex, string location) {
      lock (errHndlrLock) {
        // continue with your code
      }
    }
    

    Another thing that comes to mind: I see your Exception Handler code shows part of a try/catch block.

    Q. What do you do with the Exception you catch?

    If you are recursively calling the same method, this could be a cause of you losing information.

    If you wanted to take a different approach, you could use a Queue, defined global to the class, along with your StreamWriter:

    class CCR {
    
      private Queue<string> m_queue;
      private StreamWriter m_writer;
    
      public CCR() {
        m_queue = new Queue<string>();
        m_writer = new StreamWriter(string.Format("Log_{0}.txt", DateTime.Now.ToFileTime()));
      }
    
      public void ExceptionHandler(Exception ex, string location) {
        string item = string.Format("{0:u}: {1}\r\n\t\t{2}", DateTime.Now, location, ex.Message);
        if (ex.InnerException == null) {
          m_queue.Enqueue(item);
        } else {
          m_queue.Enqueue(item + string.Format("\r\n\t\tInner Exception: {0}", ex.InnerException.Message));
        }
        QueueHandler();
      }
    
      private void QueueHandler() {
        while (0 < m_queue.Count) {
          m_writer.WriteLine(m_queue.Dequeue());
        }
      }
    
      public void Close() {
        QueueHandler();
        m_writer.Flush();
        m_writer.Close();
        m_writer.Dispose();
      }
    

    Just make sure you call Close() before your code exits