Single thread apartment issue - c#

From my mainform I call the following to open a new form
MyForm sth = new MyForm();
sth.show();
Everything works great however this form has a combobox that, when I switch its AutoCompleteMode to suggest and append, I got this exception while showing the form:
Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.
I have set this attribute on my main function as requested by the exception:
[STAThread]
static void Main(string[] args)
{ ...
Can I please get some help as to understand what might be wrong.
Sample code:
private void mainFormButtonCLick (object sender, EventArgs e)
{
// System.Threading.Thread.CurrentThread.SetApartmentState(ApartmentState.STA); ?
MyForm form = new MyForm();
form.show();
}
Designer:
this.myCombo.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
this.myCombo.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems;
this.myCombo.FormattingEnabled = true;
this.myCombo.Location = new System.Drawing.Point(20, 12);
this.myCombo.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.myCombo.Name = "myCombo";
this.myCombo.Size = new System.Drawing.Size(430, 28);
this.myCombo.Sorted = true;
this.myCombo.TabIndex = 0; phrase";
Setting data source
public MyForm(List<string> elem)
{
InitializeComponent();
populateColorsComboBox();
PopulateComboBox(elem);
}
public void PopulateComboBox(List<string> list )
{
this.myCombo.DataSource = null;
this.myCombo.DisplayMember = "text";
this.myCombo.DataSource = list;
}

Is Main(string[] args) really your entry point?
Maybe you have another Main() overload with no parameters. Or some other Main() in another class. Please open Project properties and look for the start object.

Windows Forms applications must be run in the STA method.
See here: Could you explain STA and MTA?
And COM comes into play since windows forms comes into play since the controls themselves use native windows handles and thus must adhere to the STA model. I do believe that the reason you get the error at this specific place is that a second thread is created/ used internally by the AutoCompletion.
And as far as I have expereienced, the threading model must be set in Main, changing it later only works from STA to MTA, but not the other way round

As a wild thought: Create a deep copy of your source List in your second form and bind the combobox to the copy of the list rather than the original.

Related

WinForms threading invoke handling

This code is running from other thread than the thread it was created on.
Thread gets create from the constructor of StartScanning
public StartScanning()
{
InitializeComponent();
Thread _IMSS_THREAD = new Thread(_IMSS_START_SCANNING);
_IMSS_THREAD.IsBackground = true;
_IMSS_THREAD.Start();
}
Main form
StartScanning _IMSS_START_SCANNING = StartScanning._IMSS_CREATE_CONTROLE();
_IMSS_START_SCANNING._IMSS_ON_ALL_SCAN_COMPLETE += _IMSS_ON_SCAN_COMPLETE;
this._IMSS_MainPanel.Controls.Add(_IMSS_START_SCANNING);
On scan complete user control, this code is in main form:
ScanComplete _IMSS_ON_COMPLETE = new ScanComplete();
public void _IMSS_ON_SCAN_COMPLETE(ref List<BetterListViewGroup> _IMSS_LIST_OF_GROUP_TARGETS)
{
List<BetterListViewGroup> IMSS_LIST_OF_GROUP_TARGETS = _IMSS_LIST_OF_GROUP_TARGETS;
_IMSS_ON_COMPLETE._IMSS_AddRangeTargets(ref IMSS_LIST_OF_GROUP_TARGETS);
this.Invoke(new MethodInvoker(() =>
{
this._IMSS_MainPanel.Controls.Clear();
this._IMSS_MainPanel.Controls.Add(_IMSS_ON_COMPLETE);
}));
}
If you take a look on this code, it runs OK but it's supposed to throw
Cross-thread operation not valid, cause when we start the program this UserControl
ScanComplete _IMSS_ON_COMPLETE = new ScanComplete();
Get created on the main thread (it's global) and when we use
_IMSS_ON_COMPLETE._IMSS_AddRangeTargets(ref IMSS_LIST_OF_GROUP_TARGETS);
It adds a list of groups of listview to it, and it's out of the invoke section, but it's not throwing thread error, Why it's not throwing errors?
Try this in constructor of form:
public StartScanning()
{
InitializeComponent();
StartScanning.CheckForIllegalCrossThreadCalls = false;
}
Remember that this is not proper way to do this but this will help you to solve your problem. Search for Thread safe calling.

How to access & use an object which has been created on a different thread in WPF

I am having following scenario that I need to show preview option in my application like what ms-word does. When we click the info option under File menu item, then preview of document is shown.
In the same way, I also want to show the preview of my data rendering part in my application when someone clicks File\Info panel. For this i have written a method which gets the preview or screenshots of my app but that method is taking some time So when somebody click on the File menu then application hangs for a while. So, i tried to call that method on different thread using background worker as well as normal thread mechanism. but the thing is that method I am calling on different thread it returns an image source object and when I try to access that object on run worker completed event of background worker, then it shows an exception like owner of this object is a different thread which means that returned image has been created on a different thread therefore I can't use it. So, what is the optimized way to get and use that image in my case.
Code tends to be like this.
public void ShowPreview()
{
ImageSource source =null;
var bgWorkerThread = new BackgroundWorker()
bgWorkerThread.DoWork +=(SENDER,ARGS)=> {
source = planView.GetPreviewImage();
}
bgWorkerThread.RunWorkerCompleted += (sender,args)=>
{
// Application crashes at this point
infoPanel.PreviewImage.source = args.Result as ImageSource;
}
}
you can use invoke or you could create a "storage class" (i think its called a singleton but I'm not sure) reuse the same instance across several classes and/or threads like this.
class Test
{
void main()
{
newThread nt = new newThread();
Storage store = new Storage();
nt.store = store;
Thread t = new Thread(new ThreadStart(nt.runMe));
t.Start();
}
}
public class newThread
{
public Storage store;
public void runMe()
{
store.someNum = 8;
}
}
public class Storage
{
public int someNum;
}

Access form variables

When using a basic form application in C# I am having trouble accessing the variabels within it.
So with in the form class I have
public partial class pingerform : Form
{
..
..
private System.Windows.Forms.TextBox textBox2;
public string textBox2Text
{
get { return textBox2.Text; }
set { textBox2.Text = value; }
}
And then in the main application I have
Application.Run(new pingerform());
...
...
pingerform.textBox2Text.text() = str;
but I am told that there is no object reference.
Error 1
An object reference is required for the non-static field,
method, or property
'pingerform.textBox2Text.get' C:\Users\aaron.street\Documents\Visual
Studio 11\Projects\PingDrop\PingDrop\Program.cs 54 21 PingDrop
So I thought I would make the pinger form class static but it wont let me do this?
Error 1
Cannot create an instance of the static class
'PingDrop.pingerform' C:\Users\aaron.street\Documents\Visual Studio
11\Projects\PingDrop\PingDrop\Program.cs 21 29 PingDrop
How can I access the forms properties with out creating a specific instance of the form,
I have a background thread running that I want to update a text filed with in the form at regular intervals?
Cheers
Aaron
You have no choice but to create new instance and either pass it as parameter to the thread, or store it as member of your main Program class.
Example for the second option:
private static pingerform myPingerform = null;
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
myPingerform = new pingerform();
Thread thread = new Thread(new ThreadStart(UpdateTextBox));
thread.Start();
Application.Run(myPingerform);
}
private static void UpdateTextBox()
{
while (true)
{
myPingerform.textBox2.Text = DateTime.Now.Ticks.ToString();
Thread.Sleep(1000);
}
}
And don't forget to change the textbox to be public.
Note: this is simple working solution to the simple case of one background thread accessing the textbox. If you have more threads accessing it, this will break. For best practice methods that require some more work, please read this.
You cannot access properties of an instance without creating that instance, it is nonsense (or VB which is the same). And you have already created the instance which you then passed to Application.Run(). And anyway you cannot do anything with your form after Application.Run() because it returns only when app exits. If you want to do anything with the form you need to do that in some other places. And of course you cannot make the form class static because you need to create instances.
If you need to do something with a form in another thread, you need to pass the form instance to the thread when you create it. Note though that direct messing with GUI elements from non-GUI threads is a bad idea, you should use Control.BeginInvoke().
Please try this:
pingerform myForm = new pingerform();
Application.Run(myForm);
myForm.textBox2Text = "this is text";

C# winform backgroundworker

I am currently working on a home project for myself.
The program is written in C# using winforms.
The problem I'm currently experiencing is as followed:
I have a listview in my mainform called lvwGames
When I run the program without debugging, it runs fine.
However when I start with a debug, I get an error. This has something to do with the background worker thread.
Allow me to post some code to assist me.
private void MainViewLoad(object sender, EventArgs e)
{
RefreshGamesListView();
}
Nothing special here.
The reason I am calling RefreshGamesListView() is because I have to refresh on several occasions.
The method being called looks like this.
public void RefreshGamesListView()
{
pbRefreshGamesList.Value = 0;
bgwRefreshList.RunWorkerAsync();
}
So when the method is called, the background worker is called and runs the dowork method.
This one is quite big.
private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
List<Game> games = _mainController.RetrieveAllGames();
int count = 1;
foreach (Game game in games)
{
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
li.SubItems.Add(game.Genre.name);
li.SubItems.Add(game.Publisher.name);
li.SubItems.Add(game.Platform.name);
li.SubItems.Add(game.CompletionType.name);
li.SubItems.Add(game.gameNotice);
lvwGames.Items.Add(li);
double dIndex = (double)(count);
double dTotal = (double)games.Count;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
count++;
bgwRefreshList.ReportProgress(iProgressPercentage);
}
}
When i run the code in debug, when the code is on lvwGames.Items.Add(li);
It gives me the following error:
Cross-thread operation not valid: Control 'lvwGames' accessed from a thread other than the thread it was created on.
I have absolutely no clue why.
I think it is code specific. But it can also mean I don't get the background worker completely, and specifically when to use it properly.
The reason I'm using it is because I'm loading a large large list from the database, I want to keep responsiveness in the UI when the list is loaded, and inform the users how far it is, using a progress bar.
If any code is missing, or you actually understand why this is happening PLEASE explain me why in this case its causing the error. You don't need to fix it for me. I just want to know WHY it's caused.
Thanks for taking the time to read this post. I hope to be able to continue using the debugger soon. :)
You need to call Conrol.Invoke when accessing visual controls from background threads.
if (_lvwGames.IsHandleCreated) {
Action addGameToList = () => {
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
....
_lvwGames.Items.Add(li);
};
if (_lvwGames.InvokeRequired) {
_lvwGames.Invoke(addGameToList);
} else {
addGameToList();
}
}
From Manipulating Controls from Threads
...For example, you might call a method that disables a button or
updates a display on a form in response to action taken by a thread.
The .NET Framework provides methods that are safe to call from any
thread for invoking methods that interact with controls owned by other
threads. The Control.Invoke method allows for the synchronous
execution of methods on controls...
This is because you're attempting to access a UI control (lvwGames) from a background thread. The way to make it work requires you to marshal the information back to the main UI thread and update the control from there:
private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
List<Game> games = _mainController.RetrieveAllGames();
int count = 1;
foreach (Game game in games)
{
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
li.SubItems.Add(game.Genre.name);
li.SubItems.Add(game.Publisher.name);
li.SubItems.Add(game.Platform.name);
li.SubItems.Add(game.CompletionType.name);
li.SubItems.Add(game.gameNotice);
// This is the new line you need:
lvwGames.Invoke(new MethodInvoker(delegate { lvwGames.Items.Add(item) }));
double dIndex = (double)(count);
double dTotal = (double)games.Count;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
count++;
bgwRefreshList.ReportProgress(iProgressPercentage);
}
}
Normally you would check the InvokeRequired property first as mentioned in other answers, but there is really no need if you are always calling it from the background thread. Your DoWork method will always require an invoke call, so you might as well just go ahead and write it like that.
This happening cause, just like compiler cliams, you are going to update UI control content from another thread. You can not do that, as UI control can be updated only within main thread.
Please have look on this SO answer with example code provided:
Invoke from another thread
The background worker is not working properly if you run in debug mode in studio. If you have calls that use the windows handle to retrieve messages, then they will fail. If you for instance have a progressChanged event handler and this changes a text in a textbox that might fail.
I had this scenario: A Form that has a background worker. If I just start the worker without getting a dialog box up first then it works ok. If I show a dialog and then start the background worker then it fails. When I run the program normally it does not fail. It is somehow the debug environment that destroys the link between the events and the foreground window. I have changed my code to use invoke, and now all works both in when running in release and when I debug.
Here is a link explaining what can be done to make a program thread safe.
http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx
I did not do the same as the sample to microsoft. I made delegates, assigned to the functions I needed to run. and called invoke on them.
sample pseudo code:
class MyClassWithDelegates
{
public delegate void ProgressDelegate( int progress );
public ProgressDelegate myProgress;
public void MyProgress(int progress)
{
myTextbox.Text = ..... ; // this is code that must be run in the GUI thread.
}
public MyClassWithDelegates()
{
myProgress = new ProgressDelegate(MyProgress);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Invoke( myProgress, e.ProgressPercentage );
}
}
All code that potentially have to be run in the GUI thread of the application must be Invoked to be safe.

invoking a webbrowser to the second tab page visual c#

hey i am trying to create two webbrowsers on a sperate thread from the rest of the form. one goes to tabpage1 and the other is added to tabpage2. the first browser creates fine to page1 however the second browser wont add and the error "Unable to get the window handle for the 'WebBrowser' control. Windowless ActiveX controls are not supported." happens. heres my code:
private Thread t;
WebBrowser webBrowser1, webBrowser2;
public delegate void Addc1(Control o);
public delegate void Addc2(Control o);
public Addc1 AddControl1;
public Addc2 AddControl2;
public Form1()
{
InitializeComponent();
AddControl1 = new Addc1(AddTabControl1);
AddControl2 = new Addc2(AddTabControl2);
}
private void button2_Click(object sender, EventArgs e)
{
t = new Thread(new ThreadStart(this.UIStart));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
public void UIStart()
{
WebBrowser webBrowser1 = new WebBrowser();
webBrowser1.Location = new System.Drawing.Point(1,1);
webBrowser1.Size = new System.Drawing.Size(936, 35);
webBrowser1.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.webBrowser2_DocumentCompleted);
tabPage1.Invoke(AddControl1, new Object[] { webBrowser1 });
WebBrowser webBrowser2 = new WebBrowser();
webBrowser2.Location = new System.Drawing.Point(1,1);
webBrowser2.Size = new System.Drawing.Size(936, 935);
webBrowser2.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.webBrowser2_DocumentCompleted);
tabPage2.Invoke(AddControl2, new Object[] { wedBrowser2 });
webBrowser1.Navigate("www.ask.com");
webBrowser2.Navigate("www.google.com");
}
public void AddTabControl1(Control o)
{
tabPage1.Controls.Add(o);
}
public void AddTabControl2(Control o)
{
tabPage2.Controls.Add(o);
}
}
as i said the webbrowser1 will create and navigate but the other one will add to the controls on page2 but will not create. any ideas?
thanks adds
You are violating several threading rules:
WebBrowser is an ActiveX control with single-threaded apartment requirements. You are correctly setting the thread to STA but you are violating the second requirement: an STA thread must pump a message loop. You didn't get that far yet, but the DocumentCompleted event will never fire.
Windows requires child controls to belong to the same thread as their parent. In your case, there's a nasty mismatch. The AxHost wrapper will be created on the UI thread due to the Controls.Add() call but the native window handle that the WebBrowser uses may not. I think that's the source of the exception you get.
You can't make this work as you intended, a web browser is simply not a chunk of code that can handle multiple threads. Even if you do create it on the correct thread, calls made on a background thread will be marshaled by COM to implement the STA contract, there is no concurrency.
Using it on a separate STA thread that pumps a message loop (Application.Run) is fine but the form and its controls must be created on that same thread.

Categories