c# - Logging Window in MTA thread: Access Violation -


in our app have tracing window can enable on client locations allow debugging, accessed thought static library.

problem is, when there lot of log messages going window crashes accessviolation error. link of code crashes richtextbox.appendtext(..,..,..).

here create window.

public static void start(form parent) {   if (_runningthread == null || !_runningthread.isalive)   {        _runningthread = new thread(() =>           {               _traceview = new traceview(parent) { text = "tracing ~ " + parent.text };               application.run(_traceview);            });        _runningthread.setapartmentstate(apartmentstate.mta);       _runningthread.start();   }  } 

and here write line textbox

public void write(string line, color color) {   try   {       _msgqueue.enqueue(new tuple<string, color>(line, color));        methodinvoker gui = delegate           {               try               {                   // getting overflow trim out lines                   if (uitrace.lines.length > 5000)                   {                       uitrace.lines = new string[0];                       uitrace.selectionstart = uitrace.textlength;                       application.doevents();                   }                    while (_msgqueue.count != 0)                   {                        bool retry;                       var count = 0;                                             {                           try                           {                               count++;                               if (_indent < 0)                                   _indent = 0;                                var msg = _msgqueue.dequeue();                               var selectionstart = uitrace.textlength;                               uitrace.appendtext(string.format("[{0}] {1}{2}", _stopwatch.elapsedmilliseconds, string.empty.padleft(_indent * 4), msg.item1));                                 uitrace.select(selectionstart, uitrace.textlength);                               uitrace.selectioncolor = msg.item2;                               uitrace.selectionstart = uitrace.textlength;                               uitrace.scrolltocaret();                               retry = false;                           }                           catch (exception)                           {                               retry = true;                           }                       } while (retry && count < 5);                   }               }               catch (exception)               {                   // don't care exceptions in here, anyway               }           };        if (uitrace.invokerequired && !uitrace.disposing && !uitrace.isdisposed)       {           uitrace.begininvoke(gui);           return;       }       gui();   }   catch (exception)   {     //   qit_backoffice.processes.errors.errorhandler.writeerrorlog(sources.sourceenum.external, ex, "error writing trace");   } } 

i have no idea how around one, thought calling begininvoke() needed.

looking possible, or if knows third party tool handle better happy @ that.

below modification of logger. note how _processing , lock used avoid reentrancy , protect _queue. also, use synchronizationcontext instead of control.begininvoke avoid dependency on window disposition state. traceview can created (with traceview.create) , used thread, window belongs parent window's thread , that's it's delivering text richedit. it's possible have dedicated sta thread that, don't feel that's necessary.

[edited] i've eliminated might race condition in checking _processing , added createonownthread in case dedicated thread logger ui requirement. decided keep application.doevents() cases when write called tight loop, keep ui responsive.

usage (stress-test):

private void form1_load(object sender, eventargs ev) {     var traceview = traceview.create(this);     (var = 0; < 1000; i++)     {         var _i = i;         task.run(() =>          {             traceview.write(string.format("line: {0}\n", _i), system.drawing.color.green);         });     } } 

implementation:

using system; using system.collections.generic; using system.diagnostics; using system.drawing; using system.linq; using system.threading; using system.threading.tasks; using system.windows.forms;  namespace logger {     public partial class traceview : form     {         private form _parent = null;          private synchronizationcontext _context = synchronizationcontext.current;         private int _threadid = thread.currentthread.managedthreadid;          private object _lock = new object(); // sync lock protect _queue , _processing         private queue<tuple<string, color>> _queue = new queue<tuple<string, color>>();         private volatile bool _processing = false; // reentracy check flag          public traceview(form parent)         {             _parent = parent;             initializecomponent();         }          public static traceview create(form parent)         {             traceview view = null;             // create on parent window's thread             parent.invoke(new action(() => {                 view = new traceview(parent);                 view.show(parent);             }));             return view;         }          private void dequeuemessages()         {             // make sure on ui thread             debug.assert(thread.currentthread.managedthreadid == _threadid);               lock (_lock)             {                 // prevent re-entracy                 if (_processing)                     return;                 // mark beginning of processing                 _processing = true;             }              // process pending messages             (; ; )             {                 tuple<string, color> msg = null;                  lock (_lock)                 {                     if (!_queue.any())                     {                         // mark end of processing                         _processing = false;                         return;                     }                     msg = _queue.dequeue();                 }                  if (this.disposing || this.isdisposed)                 {                     // not loose messages if window disposed                     trace.write(msg.item1);                  }                 else                 {                     var selectionstart = _richtextbox.textlength;                     _richtextbox.appendtext(msg.item1);                     _richtextbox.select(selectionstart, _richtextbox.textlength);                     _richtextbox.selectioncolor = msg.item2;                     _richtextbox.selectionstart = _richtextbox.textlength;                     _richtextbox.scrolltocaret();                     _richtextbox.refresh(); // redraw;                     // doevents required if logging tight loop,                      // keep ui responsive                     application.doevents();                  }             }         }          public void write(string line, color color)         {             lock (_lock)             {                 _queue.enqueue(new tuple<string, color>(line, color));                 // prevent re-entracy                 if (_processing)                     return; // dequeuemessages in progress             }              if (thread.currentthread.managedthreadid == _threadid)                 dequeuemessages();             else                 _context.post((_) =>                  {                      dequeuemessages();                   }, null);         }          public static traceview createonownthread()         {             traceview view = null;             using (var sync = new manualreseteventslim())             {                 // create on own thread                 var thread = new thread(() =>                 {                     synchronizationcontext.setsynchronizationcontext(new windowsformssynchronizationcontext());                     view = new traceview(null);                     view.show();                     sync.set(); // ready write calls                     application.run(view); // view application.exitthread() when closed                     return;                 });                  thread.setapartmentstate(apartmentstate.sta);                 thread.start();                 sync.wait();             }             return view;         }      } }