1

I'm trying to use this Converting between RTF and HTML library from MSDN to convert some RTF text to HTML. The jist of my setup is an AJAX call from JavaScript to a C# handler which calls this MarkupConverter library to do the conversion, then write back the HTML.

Here's my JavaScript:

$.ajax({
   type: "POST",
   url: "MyHandler.ashx",
   data: richTextData,
   success: function (html) {
            alert('success, html: ' + html);
   },
   error: function (msg) {
            alert("error: " + msg);
   }
});

And the code from my handler, which is also very simple:

public void ProcessRequest(HttpContext context)
{
   if (context.Request.Form.Count > 0)
   {
      string rtf = context.Request.Form[0];
      string html = "";
      if (rtf != "")
      {
         markupConverter = new MarkupConverter.MarkupConverter();
         html = markupConverter.ConvertRtfToHtml(rtf);
      }
      if (html != "")
      {
         context.Response.ContentType = "text/html";
         context.Response.Write(html);
      }
      else
      {
         context.Response.ContentType = "text/plain";
         context.Response.Write("Error from RTF2HTML");
      }
   }
}

The problem is, every time this runs, an Exception is thrown because the RichTextBox control is getting created on a background thread:

[InvalidOperationException: The calling thread must be STA, because many UI components require this.]
System.Windows.Input.InputManager..ctor() +11032206
System.Windows.Input.InputManager.GetCurrentInputManagerImpl() +125
System.Windows.Input.KeyboardNavigation..ctor() +185
System.Windows.FrameworkElement.EnsureFrameworkServices() +109
System.Windows.FrameworkElement..ctor() +504
System.Windows.Controls.Control..ctor() +87
System.Windows.Controls.RichTextBox..ctor(FlowDocument document) +56
MarkupConverter.RtfToHtmlConverter.ConvertRtfToXaml(String rtfText) +67 MarkupConverter.RtfToHtmlConverter.ConvertRtfToHtml(String rtfText) +23 MyHandler.ProcessRequest(HttpContext context) +416

I thought maybe because the AJAX call is asychronous, the call is getting placed on a background thread. So I changed it to this:

var postText = $.ajax({
   type: "POST",
   url: "RTF2HTML.ashx",
   data: textData,
   async: false
}).responseText;
alert(postText);

But even still when I check the current thread in my handler:

context.Response.Write("thread: " + System.Threading.Thread.CurrentThread.GetApartmentState().ToString());

It still returns MTA.

Is there a way to hook into the main STA thread, or will I have to create a new thread and specify STA? If that's the case, how can I set the callback function up to return my HTML the way Response.Write currently does?

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
lhan
  • 4,585
  • 11
  • 60
  • 105

2 Answers2

3

This might be useful:

How to run something in the STA thread?

Perhaps you could make the call to...

html = markupConverter.ConvertRtfToHtml(rtf);

...on a different thread in the same way?

string rtf;

public void ProcessRequest(HttpContext context)
{
   if (context.Request.Form.Count > 0)
   {
      rtf = context.Request.Form[0];
      string html = "";
      if (rtf != "")
      {
         Thread thread = new Thread(ConvertMarkup);
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();
      }
      if (html != "")
      {
         context.Response.ContentType = "text/html";
         context.Response.Write(html);
      }
      else
      {
         context.Response.ContentType = "text/plain";
         context.Response.Write("Error from RTF2HTML");
      }
   }
}

void ConvertMarkup()
{
    markupConverter = new MarkupConverter.MarkupConverter();
     html = markupConverter.ConvertRtfToHtml(rtf);
}
Community
  • 1
  • 1
Henners
  • 182
  • 9
  • Will execution wait after `thread.Join()` so that the `if (html != '')` line doesn't execute until after `ConvertMarkup()` finishes? – lhan Apr 24 '13 at 15:13
  • 2
    No problem, glad to help! thread.Join itself signals the calling thread to wait until the thread has finished executing...simple but does the job. – Henners Apr 24 '13 at 15:24
0
    using System.Threading; 

    Thread t = new Thread(new ThreadStart(ProcessRequest)); 

    // Make sure to set the apartment state BEFORE starting the thread. 
    t.ApartmentState = ApartmentState.STA; 
    t.Start(); 

    public void ProcessRequest(HttpContext context)
    {
       if (context.Request.Form.Count > 0)
       {
        string rtf = context.Request.Form[0];
        string html = "";
        if (rtf != "")
        {
           markupConverter = new MarkupConverter.MarkupConverter();
           html = markupConverter.ConvertRtfToHtml(rtf);
        }
        if (html != "")
        {
           context.Response.ContentType = "text/html";
           context.Response.Write(html);
        }
        else
        {
           context.Response.ContentType = "text/plain";
           context.Response.Write("Error from RTF2HTML");
        }
     }
  }
Jeff
  • 972
  • 5
  • 11
  • although, obviously this WILL create a new thread, but there is no way around it. There is a lot of Googling you can do to see why this is required for the UI. – Jeff Apr 24 '13 at 15:04
  • Thanks. One issue - syntax error on the `new ThreadStart` line, "No overload for 'ProcessRequest' matches delegate 'System.Threading.ThreadStart'". Any ideas? – lhan Apr 24 '13 at 15:06
  • Yes...sorry my bad. Loose the HttpContext parameter for your function. You can not pass the function a parameter. – Jeff Apr 24 '13 at 15:16