javamail imap SortTerm example - sorting

I want to use imapFolder.getSortedMessages(SortTerm[] term); method to get mail and sorted by sent date.
public List<MailMessage> load(SortTerm[] term,)
throws MessagingException, UnsupportedEncodingException {
List<MailMessage> mailMessages = new ArrayList<MailMessage>();
term[0] = SortTerm.DATE;
Store store = getStore();
Folder folder = store.getFolder("INBOX");
if (folder != null) {
IMAPFolder imapFolder = (IMAPFolder) folder;
imapFolder.open(Folder.READ_WRITE);
Message[] messages = imapFolder.getSortedMessages(term);
FetchProfile fp = new FetchProfile();
fp.add(UIDFolder.FetchProfileItem.UID);
fp.add(FetchProfile.Item.ENVELOPE);
folder.fetch(messages, fp);
UIDFolder uidFolder = (UIDFolder) folder;
for (Message message : messages) {
Long uid = uidFolder.getUID(message);
MimeMessage msg = (MimeMessage) message;
MailMessage mailMessage = new MailMessage(msg, uid);
mailMessages.add(mailMessage);
}
}
return mailMessages;
}
but it not work for me.It don't sort by sent date.
Any suggestion for my program.

Related

Add fax number to exchage contanct using EWS API from FAX and Scan

I try Mictosoft example to create and and contact to exchange server using EWS and strangly mark as invalid while try using fax number for sending using Windows Fax and Scan (wfs.exe).
So , Does anybody know how should I set fax number in EWS that can be usable for sending fax in fax and scan?
After a lot of exprementing and invaestigating using OutlookSpy I found out I should add a # before number to be recognizable by Windows Fax and Scan. This is my final code.
public static ExchangeService GetService(string emailUser = defaultUser, string pass = defaultPass)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
//service.AutodiscoverUrl(emailUser, RedirectionUrlValidationCallback);
service.Url = new Uri("https://xs3.domain.net/EWS/Exchange.asmx");
service.Credentials = new WebCredentials(emailUser, pass);
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
return service;
}
public static bool IsValidEmail(string email)
{
try
{
var addr = new System.Net.Mail.MailAddress(email);
return addr.Address == email;
}
catch
{
return false;
}
}
public static void AddOrUpdateContact(ExchangeService service, string id, Dictionary<string, string> dataRecord, string targetFolder = defaultCotactsFolder, bool faxOnly = false)
{
Folder parentFolder;
FindFoldersResults folderItems = service.FindFolders(
WellKnownFolderName.Contacts,//parent folder
new SearchFilter.IsEqualTo(FolderSchema.DisplayName, targetFolder),
new FolderView(1)// return first 1 records
);
if (folderItems != null && folderItems.Count() > 0)
{//folder found!
parentFolder = folderItems.First() as Folder;
}
else
{//folder NOT found!
return;
}
ExtendedPropertyDefinition CustomProperty = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "BusinessFax", MapiPropertyType.String);
FindItemsResults<Item> contactItems = service.FindItems(
parentFolder.Id,
new SearchFilter.IsEqualTo(ContactSchema.NickName, id),
new ItemView(1)
);
bool doesExist = contactItems != null && contactItems.Count() > 0;
Contact contact;
if (doesExist)
{//contact found!
contact = contactItems.First() as Contact;
}
else
{
// Create the contact.
contact = new Contact(service);
}
// Specify the name and how the contact should be filled.
contact.CompanyName = name;
contact.NickName = id;
contact.PhoneNumbers[PhoneNumberKey.BusinessFax] = "#"+dataRecord["COD_FAX_SUPID"];
contact.FileAsMapping = FileAsMapping.Company;
if (!faxOnly)
{
// Specify the business, home, and car phone numbers.
contact.PhoneNumbers[PhoneNumberKey.BusinessPhone] = dataRecord["COD_TEL_SUPID"];
if (IsValidEmail(dataRecord["EMAIL_SUPID"]))
{
// Specify two email addresses.
contact.EmailAddresses[EmailAddressKey.EmailAddress1] = new EmailAddress(dataRecord["EMAIL_SUPID"]);
}
contact.PhysicalAddresses[PhysicalAddressKey.Home] = new PhysicalAddressEntry()
{
Street = dataRecord["DES_STREET_SUPID"],
City = dataRecord["DES_TOWN_SUPID"],
CountryOrRegion = dataRecord["DES_COUNTRY_SUPID"],
PostalCode = dataRecord["COD_POST_SUPID"] + (String.IsNullOrWhiteSpace(dataRecord["NUM_BOX_SUPID"]) ? "" : $", PostBox: {dataRecord["NUM_BOX_SUPID"]}"),
}
}
if (doesExist)
{ //contact found!
contact.Update(ConflictResolutionMode.AlwaysOverwrite);
}
else
{
//save new!
contact.Save(parentFolder.Id);
}
}
Usage Example:
ExchangeService service = ContatctUpdaterLib.ContatcUpdater.GetService("USER#DONAMAIN.COM","123456");
//GetUserData return user data record as a Dictionary<string,string>
var data = GetUserData();
AddOrUpdateContact(service, dataRecord["COD_SUP_SUPID"], dataRecord, "Contacts(Fax Only)" ,true);

Send multiple files from angular typescript to spring and return as zip folder for downloading?

I want to send multiple files in an array to spring and create a zip folder for downloading
UploadController:
#Autowired
StorageService storageService;
#PostMapping("/upload")
public ResponseEntity<ResponseMessage> uploadFiles(#RequestParam("files") MultipartFile[] files) {
String message = "";
try {
storageService.zip(files);
message = "Uploaded the files successfully";
return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message));
} catch (Exception e) {
message = "Fail to upload files!";
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
}
}
StorageService
public void zip(MultipartFile[] files) {
List<Path> filepaths = new ArrayList();
for (MultipartFile file : files) {
Path filepath = Paths.get("my/tmp/dir", file.getOriginalFilename());
filepaths.add(filepath);
try (OutputStream os = Files.newOutputStream(filepath)) {
os.write(file.getBytes());
}
}
File zip = new File("path/to/my/zip");
try { zip.createNewFile(); }
FileOutputStream output = null;
try { output = new FileOutputStream(zip); }
ZipOutputStream out = new ZipOutputStream(output);
try {
for (Path filepath : filepaths) {
File f = new File(filepath);
FileInputStream input = new FileInputStream(f);
ZipEntry e = new ZipEntry(f.getName());
out.putNextEntry(e);
byte[] bytes = new byte[1024];
int length;
while((length = input.read(bytes)) >= 0) {
out.write(bytes, 0, length);
}
input.close();
}
out.close();
output.close();
}
}

Azure: How to move messages from poison queue to back to main queue?

I'm wondering if there is a tool or lib that can move messages between queues?
Currently, i'm doing something like below
public static void ProcessQueueMessage([QueueTrigger("myqueue-poison")] string message, TextWriter log)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connString);
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueClient.GetQueueReference("myqueue");
queue.CreateIfNotExists();
var messageData = JsonConvert.SerializeObject(data, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
queue.AddMessage(new CloudQueueMessage(messageData));
}
As at (2018-09-11) version 1.4.1 of the Microsoft Azure Storage Explorer doesn’t have the ability to move messages from one Azure queue to another.
I blogged a simple solution to transfer poison messages back to the originating queue and thought it might save someone a few minutes. Obviously, you'll need to have fixed the error that caused the messages to end up in the poison message queue!
You’ll need to add a NuGet package reference to Microsoft.NET.Sdk.Functions :
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
void Main()
{
const string queuename = "MyQueueName";
string storageAccountString = "xxxxxx";
RetryPoisonMesssages(storageAccountString, queuename);
}
private static int RetryPoisonMesssages(string storageAccountString, string queuename)
{
CloudQueue targetqueue = GetCloudQueueRef(storageAccountString, queuename);
CloudQueue poisonqueue = GetCloudQueueRef(storageAccountString, queuename + "-poison");
int count = 0;
while (true)
{
var msg = poisonqueue.GetMessage();
if (msg == null)
break;
poisonqueue.DeleteMessage(msg);
targetqueue.AddMessage(msg);
count++;
}
return count;
}
private static CloudQueue GetCloudQueueRef(string storageAccountString, string queuename)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageAccountString);
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueClient.GetQueueReference(queuename);
return queue;
}
Azure Storage Explorer version 1.15.0 can now do this as of 2020. https://github.com/microsoft/AzureStorageExplorer/issues/1064
Essentially Azure Storage doesn't support moving messages from one queue to another. You would need to do this on your own.
One way to implement moving the messages from one queue to another is by dequeuing the messages from the source queue (by calling GetMessages), read the contents of the message and then creating a new message in the target queue. This you can do via using Storage Client Library.
One tool that comes to my mind for moving messages is Cerebrata Azure Management Studio(paid product with 15 days free trial). It has this functionality.
As at (2018-09-11) version 1.4.1 of the Microsoft Azure Storage Explorer doesn't support moving queue messages.
Here's an updated version of Mitch's answer, using the latest Microsoft.Azure.Storage.Queue package. Simply create a new .NET Console application, add the above-mentioned package to it, and replace the contents of Program.cs with the following:
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Queue;
using System.Threading.Tasks;
namespace PoisonMessageDequeuer
{
class Program
{
static async Task Main(string[] args)
{
const string queuename = "MyQueueName";
string storageAccountString = "xxx";
await RetryPoisonMesssages(storageAccountString, queuename);
}
private static async Task<int> RetryPoisonMesssages(string storageAccountString, string queuename)
{
var targetqueue = GetCloudQueueRef(storageAccountString, queuename);
var poisonqueue = GetCloudQueueRef(storageAccountString, queuename + "-poison");
var count = 0;
while (true)
{
var msg = await poisonqueue.GetMessageAsync();
if (msg == null)
break;
await poisonqueue.DeleteMessageAsync(msg);
await targetqueue.AddMessageAsync(msg);
count++;
}
return count;
}
private static CloudQueue GetCloudQueueRef(string storageAccountString, string queuename)
{
var storageAccount = CloudStorageAccount.Parse(storageAccountString);
var queueClient = storageAccount.CreateCloudQueueClient();
var queue = queueClient.GetQueueReference(queuename);
return queue;
}
}
}
It's still pretty slow if you're working with >1000 messages though, so I'd recommend looking into batch APIs for higher quantities.
Here's a python script you may find useful. You'll need to install azure-storage-queue
queueService = QueueService(connection_string = "YOUR CONNECTION STRING")
for queue in queueService.list_queues():
if "poison" in queue.name:
print(queue.name)
targetQueueName = queue.name.replace("-poison", "")
while queueService.peek_messages(queue.name):
for message in queueService.get_messages(queue.name, 32):
print(".", end="", flush=True)
queueService.put_message(targetQueueName, message.content)
queueService.delete_message(queue.name, message.id, message.pop_receipt)
I just had to do this again and took the time to update my snipped to the new storage SDKs. See post at https://www.bokio.se/engineering-blog/how-to-re-run-the-poison-queue-in-azure-webjobs/ for more info.
Here is the code I used
using Azure.Storage.Queues;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AzureQueueTransfer
{
internal class Program
{
// Need Read, Update & Process (full url, can create in storage explorer)
private const string sourceQueueSAS = "";
// Need Add (full url, can create in storage explorer)
private const string targetQueueSAS = "";
private static async Task Main(string[] args)
{
var sourceQueue = new QueueClient(new Uri(sourceQueueSAS));
var targetQueue = new QueueClient(new Uri(targetQueueSAS));
var queuedAny = true;
while (queuedAny)
{
Thread.Sleep(30000); // Sleep to make sure we dont build too much backlog so we can process new messages on higher prio than old ones
queuedAny = false;
foreach (var message in sourceQueue.ReceiveMessages(maxMessages: 32).Value)
{
queuedAny = true;
var res = await targetQueue.SendMessageAsync(message.Body);
Console.WriteLine($"Transfered: {message.MessageId}");
await sourceQueue.DeleteMessageAsync(message.MessageId, message.PopReceipt);
}
Console.WriteLine($"Finished batch");
}
}
}
}
To anyone coming here looking for a Node equivalent of #MitchWheats answer using an Azure Function.
import AzureStorage from 'azure-storage'
import { Context, HttpRequest } from '#azure/functions'
import util from 'util'
const queueService = AzureStorage.createQueueService()
queueService.messageEncoder = new AzureStorage.QueueMessageEncoder.TextBase64QueueMessageEncoder()
const deleteMessage = util.promisify(queueService.deleteMessage).bind(queueService)
const createMessage = util.promisify(queueService.createMessage).bind(queueService)
const getMessage = util.promisify(queueService.getMessage).bind(queueService)
export async function run (context: Context, req: HttpRequest): Promise<void> {
try {
const poisonQueue = (req.query.queue || (req.body && req.body.queue));
const targetQueue = poisonQueue.split('-')[0]
let count = 0
while (true) {
const message = await getMessage(poisonQueue)
if (!message) { break; }
if (message.messageText && message.messageId && message.popReceipt) {
await createMessage(targetQueue, message.messageText)
await deleteMessage(poisonQueue, message.messageId, message.popReceipt)
}
count++
}
context.res = {
body: `Replayed ${count} messages from ${poisonQueue} on ${targetQueue}`
};
} catch (e) {
context.res = { status: 500 }
}
}
To use the function you need to you provide connection information for the storage account used for your storage queues. This is provided as environment variables. Either you provide AZURE_STORAGE_ACCOUNT and AZURE_STORAGE_ACCESS_KEY, or AZURE_STORAGE_CONNECTION_STRING. More on this is available in the Azure Storage SDK docs.
Also wrote a few lines about it in this Medium article
Updated python based on Jon Canning's answer:
from azure.storage.queue import QueueServiceClient
queueService = QueueServiceClient.from_connection_string(conn_str="DefaultEndpointsProtocol=https;AccountName=<account>;AccountKey=<key>;EndpointSuffix=core.windows.net")
for queue in queueService.list_queues():
if "poison" in queue.name:
print(queue.name)
targetQueueName = queue.name.replace("-poison", "")
queue = queueService.get_queue_client(queue=queue.name)
targetQueue = queueService.get_queue_client(queue=targetQueueName)
while queue.peek_messages() :
messages = queue.receive_messages()
for msg in messages:
targetQueue.send_message(msg.content)
queue.delete_message(msg)
As Mikael Eliasson noted, the code in IGx89 answer is broken because
AddMessageAsync will overwrite some info on the message and then
DeleteMessagAsync will give a 404. The better solution is to copy the
values into a new message for AddMessageAsync
Please see enhanced version of RetryPoisonMesssages with an ability to specify only list of messages(instead of all in a queue) and allow to copy messages instead of move them.
It also logs success/failure for each message.
/// <param name="storageAccountString"></param>
/// <param name="queuename"></param>
/// <param name="idsToMove">If not null, only messages with listed IDs will be moved/copied</param>
/// <param name="deleteFromPoisonQueue">if false, messages will be copied; if true, they will be moved
///Warning: if queue is big, keeping deleteFromPoisonQueue=false can cause the same row
///from poisonqueue to be copied more than once(the reason is not found yet)</param>
/// <returns></returns>
private static async Task<int> RetryPoisonMesssages(string storageAccountString, string queuename, string[] idsToMove=null, bool deleteFromPoisonQueue=false)
{
var targetqueue = GetCloudQueueRef(storageAccountString, queuename);
var poisonQueueName = queuename + "-poison";
var poisonqueue = GetCloudQueueRef(storageAccountString, poisonQueueName);
var count = 0;
while (true)
{
var msg = await poisonqueue.GetMessageAsync();
if (msg == null)
{
Console.WriteLine("No more messages in a queue " + poisonQueueName);
break;
}
string action = "";
try
{
if (idsToMove == null || idsToMove.Contains(msg.Id))
{
var msgToAdd = msg;
if (deleteFromPoisonQueue)
{
//The reason is that AddMessageAsync will overwrite some info on the message and then DeleteMessagAsync will give a 404.
//The better solution is to copy the values into a new message for AddMessageAsync
msgToAdd = new CloudQueueMessage(msg.AsBytes);
}
action = "adding";
await targetqueue.AddMessageAsync(msgToAdd);
Console.WriteLine(action + " message ID " + msg.Id);
if (deleteFromPoisonQueue)
{
action = "deleting";
await poisonqueue.DeleteMessageAsync(msg);
}
Console.WriteLine(action + " message ID " + msg.Id);
}
}
catch (Exception ex)
{
Console.WriteLine("Error encountered when "+ action + " " + ex.Message + " at message ID " + msg.Id);
}
count++;
}
return count;
}

Using JavaMail reading from gmail issue

I am having problems reading mails from gmail (pop3) using javamail. I have a code that woks perfectly if the mail was sent from ubuntu's Thnderbird. How ever if the mail was originally sent from mac it fails.
This is the code I ham using:
private static final String UNKNOWN_BRAND_PATH = "UNKNOWN";
public static final String FOLDER_NAME = "INBOX";
private static Logger LOG = org.slf4j.LoggerFactory.getLogger(LzMailRecieverService.class);
#Value("${lz.mail.address}")
private String lzMailUserName;
#Value("${lz.mail.password}")
private String lzMailPassword;
#Value("${lz.mail.tmp.folder}")
private String lzMailTmpFolder;
public Store connect() throws Exception {
String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
Properties pop3Props = new Properties();
pop3Props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
pop3Props.setProperty("mail.pop3.socketFactory.fallback", "false");
pop3Props.setProperty("mail.pop3.port", "995");
pop3Props.setProperty("mail.pop3.socketFactory.port", "995");
URLName url = new URLName("pop3", "pop.gmail.com", 995, "", lzMailUserName, lzMailPassword);
Session session = Session.getInstance(pop3Props, null);
Store store = new POP3SSLStore(session, url);
store.connect();
return store;
}
public Folder openFolder(Store store) throws MessagingException {
Folder folder = store.getDefaultFolder();
folder = folder.getFolder(FOLDER_NAME);
folder.open(Folder.READ_ONLY);
return folder;
}
public List<MailDetails> readAttachement(Folder folder) throws IOException, MessagingException {
Message[] messages = folder.getMessages();
List<MailDetails> mailDetails = new ArrayList<MailDetails>();
for (Message message : messages) {
logMailDetails(message);
if (message.getContent() instanceof Multipart) {
Multipart multipart = (Multipart) message.getContent();
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if (!Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition())) {
continue; // dealing with attachments only
}
InputStream is = bodyPart.getInputStream();
String uid = getUid(message);
String to = getTo(message);
String from = getFrom(message);
File d = new File(lzMailTmpFolder + File.separator + uid);
if (d.exists() == false) {
d.mkdir();
}
File f = new File(d, new DateTime().getMillis() + "-" + bodyPart.getFileName());
FileOutputStream fos = new FileOutputStream(f);
IOUtils.copy(is, fos);
MailDetails md = new MailDetails(to, from, f, uid);
mailDetails.add(md);
}
}
else {
LOG.warn("Message conteant is not Multipart " + message.getContentType() + " skipping ...");
}
}
return mailDetails;
}
private String getFrom(Message message) throws MessagingException {
Address[] froms = message.getFrom();
return froms[0].toString();
}
private String getTo(Message message) throws MessagingException {
Address[] tos = message.getAllRecipients();
return tos[0].toString();
}
public void logMailDetails(Message m) throws MessagingException {
Address[] f = m.getFrom();
if (f != null) {
for (int j = 0; j < f.length; j++)
LOG.debug("FROM: " + f[j].toString());
}
Address[] r = m.getRecipients(Message.RecipientType.TO);
if (r != null) {
for (int j = 0; j < r.length; j++) {
LOG.debug("TO: " + r[j].toString());
}
}
LOG.debug("SUBJECT: " + m.getSubject());
Date d = m.getSentDate();
LOG.debug("SendDate: " + d);
}
private String getUid(Message m) throws MessagingException {
try {
Address[] tos = m.getAllRecipients();
String to = tos[0].toString();
to = to.split("#")[0];
String[] parts = to.split("\\+");
return parts[parts.length - 1];
}
catch (Exception e) {
LOG.error("Failes to extract brand hash from email address " + Lists.newArrayList(m.getFrom()));
return UNKNOWN_BRAND_PATH;
}
}
The problem is that for the mails originally created in mac bodyPart.getDisposition() always returns null. No matter what I have tried I could not understand which part is the attachment part (this is what I really need: extracting the attachment from the mail).
I have looked all over the web to find ewhat is the reason for that and I failed to find an answer. How ever I found the below note written by Juergen Hoeller that indicates that there might be an issue here (more details here: http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/mail/javamail/MimeMessageHelper.html)
Warning regarding multipart mails: Simple MIME messages that just contain HTML text but no inline elements or attachments will work on more or less any email client that is capable of HTML rendering. However, inline elements and attachments are still a major compatibility issue between email clients: It's virtually impossible to get inline elements and attachments working across Microsoft Outlook, Lotus Notes and Mac Mail. Consider choosing a specific multipart mode for your needs: The javadoc on the
MULTIPART_MODE constants contains more detailed information.
Is there any example or explnation regarding using JavaMail if the mails are sent from Mac??
Yosi
The "disposition" is at best a hint; it's not required to be included.
These JavaMail FAQ entries might help:
How do I tell if a message has attachments?
How do I find the main message body in a message that has attachments?
What are some of the most common mistakes people make when using JavaMail?

Unauthorizedaccessexception{"Invalid cross-thread access."}...is occur

i want to short my url with bitly but an exception is occur when i want to set out string to my text block
private void button1_Click(object sender, RoutedEventArgs e)
{
ShortenUrl(textBox1.Text);
}
enum Format
{
XML,
JSON,
TXT
}
enum Domain
{
BITLY,
JMP
}
void ShortenUrl(string longURL)
{
Format format = Format.XML;
Domain domain = Domain.BITLY;
string _domain;
//string output;
// Build the domain string depending on the selected domain type
if (domain == Domain.BITLY)
_domain = "bit.ly";
else
_domain = "j.mp";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
string.Format(#"http://api.bit.ly/v3/shorten?login={0}&apiKey={1}&longUrl={2}&format={3}&domain={4}",
"username", "appkey", HttpUtility.UrlEncode(longURL), format.ToString().ToLower(), _domain));
request.BeginGetResponse(new AsyncCallback(GetResponse), request);
}
void GetResponse(IAsyncResult result)
{
XDocument doc;
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string responseString = reader.ReadToEnd();
doc = XDocument.Load(reader.BaseStream);
}
//// var x = from c in doc.Root.Element("data").Elements()
// where c.Name == "url"
// select c;
//XElement n = ((IEnumerable<XElement>)x).ElementAt(0);
// textBox2.Text = ((IEnumerable<String>)x).ElementAt(0);
lista = (from Born_rich in doc.Descendants("url")
select new a()
{
shrtenurl = Born_rich.Value
}).ToList();
output = lista.ElementAt(0).shrtenurl;
textBox2.Text = output;
//
//
// textBox2.Text = s;
}
List<a> lista = new List<a>();
String output;
}
public class a
{
public String shrtenurl { set; get; }
}
The calback from HttpWebRequest occurs on a non-UI thread. If you want to change soemthing in the UI you must do it on the UI thread. Fortunatley there is an easy way to do this. You simply use the dispatcher to invoke the code in question on the UI.
Dispatcher.BeginInvoke(() => textBox2.Text = output);

Resources