I am creating a VSIX project having TextAdornment features. Here when My CreateGeom() method executes, and try to run new Image{};, It throws exception The calling thread must be STA, because many UI components require this. I tried setting ApartmentState manually but no luck. Following is my code:
[STAThread]
internal async void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
{
string lang = getCurruntCodeLanguage();
if (lang.ToString() != "java" && lang.ToString() != "ts" && lang.ToString() != "js")
{
}
else
{
try
{
currentSnapshotText = this.view.TextBuffer.CurrentSnapshot.GetText();
this.currentSnapshotLinesList = this.view.TextBuffer.CurrentSnapshot.GetText().Split('\n');
foreach (string str in this.currentSnapshotLinesList.ToList<string>())
{
TextAdornment1.allLinesList.Add(str.Trim('\r'));
}
if (numberOfLinesBeforeEnter != this.currentSnapshotLinesList.Length)
{
boundstart = 0;
boundend = 0;
this.checkCountIfMarked = 1;
this.markthis.Clear();
if (this.image != null)
{
RemoveMarkedArea();
}
if (threadSendText != null)
{
if (threadSendText.IsAlive)
{
threadSendText.Abort();
}
}
var v = System.Threading.Thread.CurrentThread.GetApartmentState(); //returns 'STA'
threadSendText = new System.Threading.Thread(new ThreadStart(SndTextCall)); // Apartment State is 'Unknown'
threadSendText.SetApartmentState(ApartmentState.STA); // Apartment State is 'STA'
threadSendText.IsBackground = true;
threadSendText.Priority = ThreadPriority.Highest;
threadSendText.Start();
}
numberOfLinesBeforeEnter = this.currentSnapshotLinesList.Length;
}
catch (Exception exc)
{
//MessageBox.Show(exc.ToString());
}
}
}
There are other recursive methods also in SndTextCall(). All are working fine but when CreateGeom() method comes into execution, it throws an exception.
private void CreateGeom(SnapshotSpan span)
{
IWpfTextViewLineCollection textViewLines = this.view.TextViewLines;
this.geometry = textViewLines.GetMarkerGeometry(span);
if (this.geometry != null)
{
this.drawing = new GeometryDrawing(this.brush, this.pen, this.geometry);
this.drawing.Freeze();
var drawingImage = new DrawingImage(this.drawing);
drawingImage.Freeze();
image = new Image
{
Source = drawingImage,
}; // Here the exception comes
Canvas.SetLeft(image, this.geometry.Bounds.Left);
Canvas.SetTop(image, this.geometry.Bounds.Top);
this.drawingImageList.Add(image);
}
}
You can explicitly switch to the UI Thread using the VS Threading Rules or the explanations in the Cookbook for Visual Studio.
private void CreateGeom(SnapshotSpan span)
{
ThreadHelper.JoinableTaskFactory.Run(async delegate
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
// You're now on the UI thread.
// ... Create your image here
});
}
Related
I'm trying to replace my ProgressBar to a Progress Dialog using Mahapps.
So I started writing this:
private void btnClick(object sender, RoutedEventArgs e)
{
ConfRelais();
}
public async void ConfRelais()
{
var controller = await this.ShowProgressAsync("hey", "hoy");
controller.Maximum = 128;
while (flag == 0)
{
string data = RelayBoard_Port.ReadTo("\r\n");
if (data == "ok") { controller.SetMessage("Done Process");
flag = 1; }
else { controller.SetProgress(Int32.Parse(data)); }
}
await controller.CloseAsync();
}
But the progress dialog only displays when it's over.. As I'm still a beginner in c# maybe I'm missing some importants points to setup that kind of function.
You should execute the loop on a background thread:
public async void ConfRelais()
{
var controller = await this.ShowProgressAsync("hey", "hoy");
controller.Maximum = 128;
await Task.Run(() =>
{
while (flag == 0)
{
string data = RelayBoard_Port.ReadTo("\r\n");
if (data == "ok")
{
controller.SetMessage("Done Process");
flag = 1;
}
else { controller.SetProgress(Int32.Parse(data)); }
}
});
await controller.CloseAsync();
}
A single thread cannot both update the UI and execute your loop simultaneously.
You also don't really need a flag. You could just break out of the loop when you receive "ok":
while (true)
{
string data = RelayBoard_Port.ReadTo("\r\n");
if (data == "ok")
{
controller.SetMessage("Done Process");
break;
}
else { controller.SetProgress(Int32.Parse(data)); }
}
There are many questions and articles on the subject of using a .NET Queue properly within a multi threaded application, however I can't find subject on our specific problem.
We have a Windows Service that receives messages onto a queue via one thread and is then dequeued and processed within another.
We're using lock when queuing and dequeuing, and the service had run fine for around 2 years without any problems. One day we noticed that thousands of messages had been logged (and so had been queued) but were never dequeued/processed, they seem to have been skipped somehow, which shouldn't be possible for a queue.
We can't replicate the circumstances that caused it as we have no real idea what caused it considering that day was no different from any of the others as far as we're aware.
The only idea we have is to do with the concurrency of the queue. We're not using the ConcurrentQueue data-type, which we plan on using in the hope it is a remedy.
One idea, looking at the source of the Queue type, is that it uses arrays internally, which have to be resized once these buffers have reached a certain length. We hypothesised that when this is being done some of the messages were lost.
Another idea from our development manager is that using multiple threads on a multicore processor setup means that even though locks are used, the individual cores are working on the data in their local registers, which can cause them to be working on different data. He said they don't work on the same memory and seems to think lock only works as expected one a single core processor using multiple threads.
Reading more about ConcurrentQueue's use of volatile I'm not sure that this would help, as I've read that using lock provides a stronger guarantee of threads using the most up-to-date state of memory.
I don't have much knowledge on this specific subject, so my question is whether the manager's idea sounds plausible, and whether we might have missed something that's required for the queue to be used properly.
Code snippet for reference (forgive the messy code, it does need refactoring):
public sealed class Message
{
public void QueueMessage(long messageId, Message msg)
{
lock (_queueLock)
{
_queue.Enqueue(new QueuedMessage() { Id = messageId, Message = msg });
}
}
public static void QueueMessage(string queueProcessorName, long messageId, Message msg)
{
lock (_messageProcessors[queueProcessorName]._queueLock)
{
_messageProcessors[queueProcessorName].QueueMessage(messageId, msg);
_messageProcessors[queueProcessorName].WakeUp(); // Ensure the thread is awake
}
}
public void WakeUp()
{
lock(_monitor)
{
Monitor.Pulse(_monitor);
}
}
public void Process()
{
while (!_stop)
{
QueuedMessage currentMessage = null;
try
{
lock (_queueLock)
{
currentMessage = _queue.Dequeue();
}
}
catch(InvalidOperationException i)
{
// Nothing in the queue
}
while(currentMessage != null)
{
IContext context = new Context();
DAL.Message msg = null;
try
{
msg = context.Messages.SingleOrDefault(x => x.Id == currentMessage.Id);
}
catch (Exception e)
{
// TODO: Handle these exceptions better. Possible infinite loop.
continue; // Keep retrying until it works
}
if (msg == null) {
// TODO: Log missing message
continue;
}
try
{
msg.Status = DAL.Message.ProcessingState.Processing;
context.Commit();
}
catch (Exception e)
{
// TODO: Handle these exceptions better. Possible infinite loop.
continue; // Keep retrying until it works
}
bool result = false;
try {
Transformation.TransformManager mgr = Transformation.TransformManager.Instance();
Transformation.ITransform transform = mgr.GetTransform(currentMessage.Message.Type.Name, currentMessage.Message.Get("EVN:EventReasonCode"));
if (transform != null){
msg.BeginProcessing = DateTime.Now;
result = transform.Transform(currentMessage.Message);
msg.EndProcessing = DateTime.Now;
msg.Status = DAL.Message.ProcessingState.Complete;
}
else {
msg.Status = DAL.Message.ProcessingState.Failed;
}
context.Commit();
}
catch (Exception e)
{
try
{
context = new Context();
// TODO: Handle these exceptions better
Error err = context.Errors.Add(context.Errors.Create());
err.MessageId = currentMessage.Id;
if (currentMessage.Message != null)
{
err.EventReasonCode = currentMessage.Message.Get("EVN:EventReasonCode");
err.MessageType = currentMessage.Message.Type.Name;
}
else {
err.EventReasonCode = "Unknown";
err.MessageType = "Unknown";
}
StringBuilder sb = new StringBuilder("Exception occured\n");
int level = 0;
while (e != null && level < 10)
{
sb.Append("Message: ");
sb.Append(e.Message);
sb.Append("\nStack Trace: ");
sb.Append(e.StackTrace);
sb.Append("\n");
e = e.InnerException;
level++;
}
err.Text = sb.ToString();
}
catch (Exception ne) {
StringBuilder sb = new StringBuilder("Exception occured\n");
int level = 0;
while (ne != null && level < 10)
{
sb.Append("Message: ");
sb.Append(ne.Message);
sb.Append("\nStack Trace: ");
sb.Append(ne.StackTrace);
sb.Append("\n");
ne = ne.InnerException;
level++;
}
EventLog.WriteEntry("Service", sb.ToString(), EventLogEntryType.Error);
}
}
try
{
context.Commit();
lock (_queueLock)
{
currentMessage = _queue.Dequeue();
}
}
catch (InvalidOperationException e)
{
currentMessage = null; // No more messages in the queue
}
catch (Exception ne)
{
StringBuilder sb = new StringBuilder("Exception occured\n");
int level = 0;
while (ne != null && level < 10)
{
sb.Append("Message: ");
sb.Append(ne.Message);
sb.Append("\nStack Trace: ");
sb.Append(ne.StackTrace);
sb.Append("\n");
ne = ne.InnerException;
level++;
}
EventLog.WriteEntry("Service", sb.ToString(), EventLogEntryType.Error);
}
}
lock (_monitor)
{
if (_stop) break;
Monitor.Wait(_monitor, TimeSpan.FromMinutes(_pollingInterval));
if (_stop) break;
}
}
}
private object _monitor = new object();
private int _pollingInterval = 10;
private volatile bool _stop = false;
private object _queueLock = new object();
private Queue<QueuedMessage> _queue = new Queue<QueuedMessage>();
private static IDictionary<string, Message> _messageProcessors = new Dictionary<string, Message>();
}
so my question is whether the manager's idea sounds plausible
Uhm. No. If all those synchronization measures would only work on single core machines, the world would have ended in complete Chaos decades ago.
and whether we might have missed something that's required for the queue to be used properly.
As far as your description goes, you should be fine. I would look at how you found out that you have that problem. logs coming in but then vanishing without being properly dequeued, wouldn't that be the default case if I simply turned off the service or rebooted the machine? Are you sure you lost them while your application was actually running?
You declare the object to be used for the lock as private object.
If you try this:
class Program
{
static void Main(string[] args)
{
Test test1 = new Test();
Task Scan1 = Task.Run(() => test1.Run("1"));
Test test2 = new Test();
Task Scan2 = Task.Run(() => test2.Run("2"));
while(!Scan1.IsCompleted || !Scan2.IsCompleted)
{
Thread.Sleep(1000);
}
}
}
public class Test
{
private object _queueLock = new object();
public async Task Run(string val)
{
lock (_queueLock)
{
Console.WriteLine($"{val} locked");
Thread.Sleep(10000);
Console.WriteLine($"{val} unlocked");
}
}
}
You will notice that the code that lies under the lock is executed even if another thread is running inside.
But if you change
private object _queueLock = new object();
To
private static object _queueLock = new object();
It changes how your lock works.
Now, this being your issue depends on if you have multiple instances that class or everything is running withing that same class.
I'm initializing and starting five threads in my Testing Class:
[Test]
public void ReportGeneratorFiveThreadTest()
{
var threads = new List<ReportGeneratorThread>();
var logger = new Log4NetLogger(typeof(ReportGeneratorThreadTest));
for (var i = 0; i < 5; i++)
{
var estimatedReportSize = EstimatedReportSize.Normal;
var thread = new ReportGeneratorThread(logger, new ReportGenerator(20), estimatedReportSize, new ManualResetEvent(false));
thread.Name = string.Format("ReportGeneratorThread{0}", i);
threads.Add(thread);
}
threads.ForEach(t => t.Start());
}
And I'm starting all threads by calling following method in ReportGeneratorThread class:
public void Start()
{
this.running = true;
this.t = new Thread(this.GenerateReport());
this.t.SetApartmentState(ApartmentState.STA);
this.t.Start();
}
Which calls a GenerateReport() method in order to perform an operation:
public void GenerateReport()
{
var didwork = false;
try
{
didwork = this.reportGenerator.GenerateReport(this.estimatedReportSize);
}
catch (Exception e)
{
this.log.LogError(ReportGenerator.CorrelationIdForPickingReport, string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Error during report generation."), 0, e);
this.doneEvent.Reset();
Debug.WriteLine("Thread is aborted !!!");
}
finally
{
if (!didwork)
{
Thread.Sleep(Settings.Default.ReportGenerationInterval);
}
}
}
My purpose is to inform my main thread once a thread among all five threads gets aborted (an exception is thrown in GenerateReport() method) and restart it in my main thread afterwards. I have tried using ManualResetEvent for that purpose, but it seems like it is not the proper class to use for this purpose. Any approaches ?.
I have a background worker that I use to create files in the background.
I had it working so that the files were created and the UI was still responsive.
I made some changes and now I can't figure out why the background worker is locking my main thread.
Here are my background worker methods. I don't have a progress changed event.
private void filecreator_bgw_DoWork(object sender, DoWorkEventArgs e)
{
if (filecreator_bgw.CancellationPending == true)
{
e.Cancel = true;
}
else
{
myManager.createFiles((SelectedFileTypes) e.Argument);
}
}
private void filecreator_bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
//status_label.Text = "Canceled!";
}
else if (e.Error != null)
{
//status_label.Text = "Error: " + e.Error.Message;
}
else
{
// Check the file manager object to see if the files were created successfully
status_label.Text = "COMPLETE";
file_statusLabel.Text = "Files Created: " + DateTime.Now.ToShortTimeString();
System.Threading.Thread.Sleep(5000);
status_label.Text = "Click Create Files to Begin";
createfiles_button.Enabled = true;
}
}
Here is the method to create the files.
public void createFiles(SelectedFileTypes x)
{
if (string.IsNullOrEmpty(Filename) || (x.isCSV == false && x.isTAB == false && x.isXML == false))
{
filesCreated = false;
return;
}
// Declare the streams and xml objects used to write to the output files
XDocument xmlFile;
StreamWriter swCSV;
StreamWriter swTAB;
CSVFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
Path.GetFileNameWithoutExtension(Filename) + "CSV_TEST.csv";
swCSV = new StreamWriter(CSVFilename);
TABFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
Path.GetFileNameWithoutExtension(Filename) + "TAB_TEST.csv";
swTAB = new StreamWriter(TABFilename);
XMLFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
Path.GetFileNameWithoutExtension(Filename) + "XML_TEST.csv";
xmlFile = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("Crosswalk"));
xmlFile.Add(new XElement("ACCOUNTS"));
// String array for use when creating xml nodes
string[] splits;
// String used to read in a line from the input file
string line = "";
// Use a try and catch block, if any errors are caught, return false
try
{
// Read each line in the file and write to the output files
using (StreamReader sr = new StreamReader(Filename))
{
int i = 0;
while ((line = sr.ReadLine()) != null)
{
if (x.isCSV)
{
swCSV.WriteLine(line.Replace(delim, ","));
}
if (x.isTAB)
{
swTAB.WriteLine(line.Replace(delim, "\t"));
}
if (x.isXML)
{
if (i <= 0)
{
i++;
continue;
}
splits = line.Split(new string[] { delim }, StringSplitOptions.RemoveEmptyEntries);
xmlFile.Root.Add(
new XElement("ACCOUNTS",
from s in header
select new XElement(s, splits[Array.IndexOf(header, header.Where(z => z.Equals(s, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault())])
)
);
}
}
// Dispose of all objects
swCSV.Close();
swCSV.Dispose();
swTAB.Close();
swTAB.Dispose();
if (x.isXML)
{
//xmlFile.Save(Path.GetFullPath(Filename) + Path.GetFileNameWithoutExtension(Filename) + "_TEST.xml");
xmlFile.Save(XMLFilename);
}
}
}
catch (Exception)
{
filesCreated = false;
return;
}
// Return true if file creation was successfull
filesCreated = true;
}
In the do work method, I build a simple struct to determine what output file types should be made and then I pass it to the method. If I comment out that call to create the files, the UI still does not respond.
In the create files method, I build out the files based on the input file that I am transforming. I do use a LINQ statement to help build out XML tags, but the arrays holding the tags values are small, 3-5 elements depending on the file chosen.
Is there a simple solution, or should I re-design the method. If I have to re-design, what are things I should keep in mind to avoid locking the main thread.
Thanks
Here is how I call the runworkerasync method:
private void createfiles_button_Click(object sender, EventArgs e)
{
SelectedFileTypes selVal = new SelectedFileTypes();
foreach (var structVal in outputformats_checkedListBox.CheckedItems)
{
if (structVal.ToString().Equals("CSV", StringComparison.InvariantCultureIgnoreCase))
selVal.isCSV = true;
if (structVal.ToString().Equals("TAB", StringComparison.InvariantCultureIgnoreCase))
selVal.isTAB = true;
if (structVal.ToString().Equals("XML", StringComparison.InvariantCultureIgnoreCase))
selVal.isXML = true;
}
// Call the FileManager object's create files method
createfiles_button.Enabled = false;
filecreator_bgw.RunWorkerAsync(selVal);
}
UPDATE:
I updated the call to start the worker and then the call to create the files using the argument passed into the worker.
You cannot interact with most UI controls directly from a BackgroundWorker. You need to access outputformats_checkedListBox.CheckedItems from the UI thread and pass the resulting SelectedFileTypes object into the BackgroundWorker as a parameter.
Also, pleas enote that your cancellation logic really didn't do much. In order for it to work well, you need to check CancellationPending throughout the process, not just when starting.
Here is a rough example of how you should start the worker:
private void StartWorker()
{
SelectedFileTypes selVal = new SelectedFileTypes();
foreach (var structVal in outputformats_checkedListBox.CheckedItems)
{
if (structVal.ToString().Equals("CSV", StringComparison.InvariantCultureIgnoreCase))
selVal.isCSV = true;
if (structVal.ToString().Equals("TAB", StringComparison.InvariantCultureIgnoreCase))
selVal.isTAB = true;
if (structVal.ToString().Equals("XML", StringComparison.InvariantCultureIgnoreCase))
selVal.isXML = true;
}
filecreator_bgw.RunWorkerAsync(selVal);
}
private void filecreator_bgw_DoWork(object sender, DoWorkEventArgs e)
{
SelectedFileTypes selVal = (SelectedFileTypes)e.Argument;
myManager.createFiles(selVal);
}
I have successfully integrated IMAPI2 using Interop.cs into my application. I can burn CD/DVDs without any problem. However, the event handler for MsftDiscFormat2Data update does not work, so I can't get my progress bar moving.
Here is the code I found at codeproject website:
private void backgroundBurnWorker_DoWork(object sender, DoWorkEventArgs e)
{
MsftDiscRecorder2 discRecorder = null;
MsftDiscFormat2Data discFormatData = null;
try
{
//
// Create and initialize the IDiscRecorder2 object
//
discRecorder = new MsftDiscRecorder2();
var burnData = (BurnData)e.Argument;
discRecorder.InitializeDiscRecorder(burnData.uniqueRecorderId);
//
// Create and initialize the IDiscFormat2Data
//
discFormatData = new MsftDiscFormat2Data
{
Recorder = discRecorder,
ClientName = ClientName,
ForceMediaToBeClosed = _closeMedia
};
//
// Set the verification level
//
var burnVerification = (IBurnVerification)discFormatData;
burnVerification.BurnVerificationLevel = _verificationLevel;
//
// Check if media is blank, (for RW media)
//
object[] multisessionInterfaces = null;
if (!discFormatData.MediaHeuristicallyBlank)
{
multisessionInterfaces = discFormatData.MultisessionInterfaces;
}
//
// Create the file system
//
IStream fileSystem;
if (!CreateMediaFileSystem(discRecorder, multisessionInterfaces, out fileSystem))
{
e.Result = -1;
return;
}
//
// add the Update event handler
//
discFormatData.Update += discFormatData_Update;
//
// Write the data here
//
try
{
discFormatData.Write(fileSystem);
e.Result = 0;
}
catch (COMException ex)
{
e.Result = ex.ErrorCode;
MessageBox.Show(ex.Message, "IDiscFormat2Data.Write failed",
MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
finally
{
if (fileSystem != null)
{
Marshal.FinalReleaseComObject(fileSystem);
}
}
//
// remove the Update event handler
//
discFormatData.Update -= discFormatData_Update;
if (_ejectMedia)
{
discRecorder.EjectMedia();
}
}
catch (COMException exception)
{
//
// If anything happens during the format, show the message
//
MessageBox.Show(exception.Message);
e.Result = exception.ErrorCode;
}
finally
{
if (discRecorder != null)
{
Marshal.ReleaseComObject(discRecorder);
}
if (discFormatData != null)
{
Marshal.ReleaseComObject(discFormatData);
}
}
}
void discFormatData_Update([In, MarshalAs(UnmanagedType.IDispatch)] object sender, [In, MarshalAs(UnmanagedType.IDispatch)] object progress)
{
//
// Check if we've cancelled
//
if (backgroundBurnWorker.CancellationPending)
{
var format2Data = (IDiscFormat2Data)sender;
format2Data.CancelWrite();
return;
}
var eventArgs = (IDiscFormat2DataEventArgs)progress;
_burnData.task = BURN_MEDIA_TASK.BURN_MEDIA_TASK_WRITING;
// IDiscFormat2DataEventArgs Interface
_burnData.elapsedTime = eventArgs.ElapsedTime;
_burnData.remainingTime = eventArgs.RemainingTime;
_burnData.totalTime = eventArgs.TotalTime;
// IWriteEngine2EventArgs Interface
_burnData.currentAction = eventArgs.CurrentAction;
_burnData.startLba = eventArgs.StartLba;
_burnData.sectorCount = eventArgs.SectorCount;
_burnData.lastReadLba = eventArgs.LastReadLba;
_burnData.lastWrittenLba = eventArgs.LastWrittenLba;
_burnData.totalSystemBuffer = eventArgs.TotalSystemBuffer;
_burnData.usedSystemBuffer = eventArgs.UsedSystemBuffer;
_burnData.freeSystemBuffer = eventArgs.FreeSystemBuffer;
//
// Report back to the UI
//
backgroundBurnWorker.ReportProgress(0, _burnData);
}
Unfortunately, the update handler gets never called. I looked up everywhere on internet but could not find a solution. I saw some people with same problem but no one was able to answer.
The original code from Eric, http://www.codeproject.com/KB/miscctrl/imapi2.aspx?msg=2695517,
does work for some reason.
Can someone please help me?
I know, it's too late, but I've got the same problem with update callbacks.
Open Project->Properties->Application->Assembly Information and tick "Make Assembly COM-visible".