Show and ShowDialog won't work - c#

It is not like anything i have seen before.
When i call (new System.Windows.Forms.Form()).ShowDialog() a form shows for a millisecond or something and then vanishes.
I traced the calls and got This:
System.Windows.Forms.Form.Dispose
System.ComponentModel.Component.Dispose
System.Windows.Forms.Application.ThreadWindows.Dispose
System.Windows.Forms.Application.ThreadContext.DisposeThreadWindows
System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner
System.Windows.Forms.Application.ThreadContext.RunMessageLoop
System.Windows.Forms.Form.ShowDialog Esfand.Program.Main C#
I have tried anything that comes to mind to fix this.
Although I have showed a login form before trying to show this form.
I don't think there is anything special going on the login form(usual boring stuff, connect to server, send credentials, receive data).
I'm using main thread for forms. I have experience with Messaging and message loops. and i have used threads in the login form.
EDIT:
Clarification for what Cody Gray suggested:
This is what I have in void Main(string[]):
LoginForm login = new LoginForm ();
login.ShowDialog ();//works
if (login.DialogResult == DialogResult.OK)
{
MainForm f = new MainForm ();
f.ShowDialog ();//won't work
}
Creating and Showing the MainForm in a new thread made everything to just start working again.but random errors occur on each form that makes this solution not good enough.
EDIT 2:
The FormClosing event doesn't even trig.
System.Windows.Forms.Form A;
A = new Form();
A.FormClosing += new FormClosingEventHandler((sender, e) => { System.Diagnostics.Debugger.Break();/*won't work. tried Breakpoints and other stuff too*/ });
A.ShowDialog();
EDIT 3:
The HandleDestroyed event stack trace:
> Esfand.exe!Esfand.Program.Main.AnonymousMethod__1(object sender = {System.Windows.Forms.Form}, System.EventArgs e = {System.EventArgs}) Line 50 + 0x6 bytes C#
System.Windows.Forms.dll!System.Windows.Forms.Control.OnHandleDestroyed(System.EventArgs e) + 0x9e bytes
System.Windows.Forms.dll!System.Windows.Forms.Form.OnHandleDestroyed(System.EventArgs e) + 0x13 bytes
System.Windows.Forms.dll!System.Windows.Forms.Control.WmDestroy(ref System.Windows.Forms.Message m) + 0x54 bytes
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) + 0x547 bytes
System.Windows.Forms.dll!System.Windows.Forms.Form.WndProc(ref System.Windows.Forms.Message m) + 0x6d bytes
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg = 2, System.IntPtr wparam, System.IntPtr lparam) + 0x15e bytes
[Native to Managed Transition]
[Managed to Native Transition]
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DestroyHandle() + 0xf7 bytes
System.Windows.Forms.dll!System.Windows.Forms.Control.DestroyHandle() + 0x3e3 bytes
System.Windows.Forms.dll!System.Windows.Forms.Control.Dispose(bool disposing) + 0x347 bytes
System.Windows.Forms.dll!System.Windows.Forms.ContainerControl.Dispose(bool disposing) + 0x19 bytes
System.Windows.Forms.dll!System.Windows.Forms.Form.Dispose(bool disposing) + 0x26a bytes
System.dll!System.ComponentModel.Component.Dispose() + 0x1b bytes
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadWindows.Dispose() + 0xb3 bytes
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.DisposeThreadWindows() + 0x12d bytes
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData) + 0x58e bytes
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason = 4, System.Windows.Forms.ApplicationContext context = {System.Windows.Forms.Application.ModalApplicationContext}) + 0x593 bytes
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) + 0x81 bytes
System.Windows.Forms.dll!System.Windows.Forms.Form.ShowDialog(System.Windows.Forms.IWin32Window owner) + 0x765 bytes
Esfand.exe!Esfand.Program.Main(string[] a = {string[0]}) Line 51 + 0x10 bytes C#

this thing is making every single form in my program raising a unique error(e.g. `cannot register drag&drop event handler')
That's a strong hint to the core problem in your code. The RegisterDragDrop() native function will be called when you have any control whose AllowDrop property is set to true. It is called when the native window for your form is created. Unfortunately that is a very bad time for any exception to be raised if you have the 64-bit version of Windows and you forced your program to run in 32-bit mode. The subject of this answer.
There are very few good reasons for RegisterDragDrop() to fail. But one. We can already tell from your snippet that you've been tinkering with Program.cs. The boilerplate version of it looks like this:
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread] // <=== Here!!!
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
I put a Big Arrow at the line of code that matters. The [STAThread] attribute is essential in any GUI program. It tells the CLR that it should initialize COM and configure the main thread of your program to be a Single Threaded Apartment. Apartments are a very important COM implementation detail whose scope is a bit beyond this answer. If the attribute is missing then the main thread of your program joins the MTA, the multithreaded apartment. A hostile place for code that is not thread-safe, like drag and drop, the clipboard and the shell dialogs.
Forgetting to use the attribute can cause bewildering exceptions. Especially bad when your dev machine boots the 64-bit version of Vista or Win7, Windows versions that have trouble with exceptions that are raised at critical moments, as explained in the linked answer.
A proper version of your Program.Main() method that doesn't have this problem and otherwise uses recommended practices:
[STAThread] // <=== Don't forget this
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (var login = new LoginForm()) {
if (login.ShowDialog() != DialogResult.OK) return;
}
Application.Run(new MainForm());
}

Try to check if a thread exception error is being thrown. Check if you see anything in the Application_ThreadException event.
static void Main()
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
try
{
//Your existing code
}
catch (Exception ex)
{
}
}
private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
}
EDIT:
Another option would be to explicitly set the MainForm as the owner to the newForm being created.
newForm.ShowDialog(MainForm);
I have a feeling that the owner is being set to your Login form by default which was closed and this is auto-closing your new form

It looks like IMsoComponentManager.FPushMessageLoopP() will call Application.ThreadContext.DisposeThreadWindows() when there is a WM_QUIT message on the event queue.
Are you posting a quit message by any chance in your LoginForm's button event handlers?

Try to define the form as a class member. not inside a function.

Your Main method looks weird. I think you are missing a call to Application.Run(). Since you don't want the application to quit as soon as the login form is closed you might need an ApplicationContext. MSDN has an example: http://msdn.microsoft.com/en-us/library/ms157901.aspx
Another possiblity is to call Application.Run() with an invisible form as parameter which then shows the other forms (and is never closed until the application should exit), but this is a quite ugly hack in my opinion.

My understanding is that forms need to be run within an application context.
I inherited some code that launches several forms from the Main context, in the following manner:
var form1 = Form1();
Application.Run(form1);
// form returns, check public method form.ButtonPushed etc
if (form.Button1Pushed)
{
var form2 = Form2();
Application.Run(form2);
}
This would successfully launch several forms.
It doesn't feel like a very elegant way of doing things but it works...

Related

System.Drawing Out of Memory Exception On Main() Method - C#

My program is a CRM, I used Rad Ribbon Bar, so Many Buttons with images, RadGridView (which some columns contain images) and so many other controls which contain images. It's a mdi parent/child program.
In so many cases while loading a mdi child or working with some grid views the program will hang and give me this error:
OutOfMemoryException occurred in System.Drawing.dll
I tried GC.Collect() on certain parts but no success. For setting images there is no code! for example for setting an image for a button I used its properties in visual studio. I have set All other control images in this way using the properties panel in visual mode.
and These are some designer codes related to drawing:
btnCustomerList.Image = global::MyApp.Properties.Resources.CustomerList32;
gridViewCommandColumn1.Image = global::MyApp.Properties.Resources.ViewShop32;
and When The error comes after a while working with the app, it will appear in Program.cs and in the line Application.Run(new MainForm());:
static void Main()
{
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", AppDomain.CurrentDomain.BaseDirectory + "\\Settings.config");
bool ok;
Mutex m = new Mutex(true, WindowsIdentity.GetCurrent().Name.ToString().Split('\\')[1] + "MyApp", out ok);
if (ok)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// The Error will cause HERE
Application.Run(new MainForm());
GC.KeepAlive(m);
}
else
Application.Exit();
}
MainForm is the mdi parent which contains Ribbon Bar.
and this is the Full stack trace:
at System.Drawing.Image.FromHbitmap(IntPtr hbitmap, IntPtr hpalette)
at System.Drawing.Image.FromHbitmap(IntPtr hbitmap)
at System.Drawing.Icon.ToBitmap()
at System.Windows.Forms.ThreadExceptionDialog..ctor(Exception t)
at System.Windows.Forms.Application.ThreadContext.OnThreadException(Exception t)
at System.Windows.Forms.Control.WndProcException(Exception e)
at System.Windows.Forms.Control.ControlNativeWindow.OnThreadException(Exception e)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at MyApp.Program.Main() in d:\\MyApp\\Application\\MyApp\\Program.cs:line 36"
UPADTED:
the code for calling mdi-children by clicking on ribbon bar buttons is here:
private void btnCustomerList_Click(object sender, EventArgs e)
{
OpenForm(new FormCustomerList(), "Customer List");
}
private void btnCustomerRelated_Click(object sender, EventArgs e)
{
OpenForm(new FormCustomerRelated(), "Customer Related");
}
and Here is OpenForm method:
private void OpenForm(Form formType, string Caption)
{
foreach (Form nform in Application.OpenForms)
{
if (nform.GetType() == formType.GetType())
{
nform.Activate();
return;
}
}
this.MdiChildren.OfType<Form>().ToList().ForEach(x => x.Dispose());
GC.Collect();
Form form = formType;
form.MdiParent = this;
form.Dock = DockStyle.Fill;
form.Show();
this.Text = Caption;
}
and in every mdi child's form constructor, after InitializeComponent(); I wrote GC.Collect(); also. But as told in comments, the GDI objects in task manager will increase and increase till 10000 objects and then application will crash.
UPADTED: THE MOST ISSUE
It seems I have found the part which cause the most GDI objects. In every form there are some controls like textboxes, drop down list etc. I have set some rules for them, for example if user enter a textbox, its back color should be yellow and after leave it should be white again. So there is a main method which I call in form load to literate through all controls and find the target ones and add for example enter and leave events with the defined rules. something Like This:
private void FormCustomerList_Load(object sender, EventArgs e)
{
ClassCRMControls.AddEventHandler(this);
}
and inside ClassCRMControls class:
public static void AddEventHandler(Control parent)
{
foreach (Control c in parent.Controls)
{
if (c.GetType() == typeof(RadTextBox))
{
c.Enter += new EventHandler(ClassCRMControls.EnterEvent);
c.Leave += new EventHandler(ClassCRMControls.LeaveEvent);
}
else
AddEventHandler(c);
}
}
private static void EnterEvent(object sender, EventArgs e)
{
(sender as RadTextBox).TextBoxElement.TextBoxItem.BackColor = Color.FromArgb(255, 251, 147);
}
private static void LeaveEvent(object sender, EventArgs e)
{
(sender as RadTextBox).TextBoxElement.TextBoxItem.ResetValue(LightVisualElement.BackColorProperty, ValueResetFlags.Local);
}
I have found the source of the problem and it was the custom animated cursor I used for grids and other controls too. I initialize it like this:
this.Cursor = ClassObjects.CreateAnimatedCursor("C:\\aniCur.ani"));
Since I loaded this cursor from the file every time I used it in any way, more and more GDI Objects got created.
So I declared a public static cursor the main() of the respective form like this:
public static Cursor animCur = ClassObjects.CreateAnimatedCursor("C:\\aniCur.ani"));
and then whenever I need to use this cursor I just reference this object public static cursor from the form.
this.Cursor = MainForm.animCur;
That's It :)
How did I find it? I just try to remove (commenting) some codes which I suspected them, then I checked GDI objects in task manager. After some testing it became apparent that the endless loading of new cursor objects was causing the problem.
There can be multiple reasons for OutOfMemoryExceptions. I have discussed 6 of them in another question.
In this case, after the comments and the edit, it became clear that GDI issues occur as well. You can detect these issues by showing an additional column in task manager:
GDIView is a far better application for GDI Leak analysis, because it also tells you the type of the GDI handle that got lost. It also has absolute and relative counters, so you can see how many of them get lost during a particular action.
The number of GDI handles can be configured in Registry. Do not use that as a permanent solution. Instead, with the additional info from GDIView, find the piece of code that leaks the GDI object.
When you run into the limit of GDI handles, the application typically starts looking bad: things are not painted any more and you get black rectangles in some places. However, this behavior is not necessary. In OP's case, black rectangles were not part of the description.

Why does OpenFileDialogue cause failure in debug on a C# Forms Application?

I've been trying to find a solutions for this issue the past 2 days. Basically when I'm debugging my form app, I have a button with an onclick event handler. Here is the code in my handler.
private void btnBrowseReqDoc_Click(object sender, EventArgs e)
{
OpenFileDialog FileDlg = new OpenFileDialog();
FileDlg.InitialDirectory = m_ConfigFile.GetReqDocDir();
FileDlg.Filter = "All Files (*.*)|*.*|Word 97-2003 (*.doc)|*.doc|Word 2007 (*.docx)|*.docx";
FileDlg.FilterIndex = 1;
if (FileDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
txtWordReqDoc.Text = FileDlg.FileName;
SetProgressControls(false);
}
this.btnExecute.Enabled = CanEnableExecuteBtn();
lblStatus.Visible = !(CanEnableExecuteBtn());
}
About 60% to 70% of the time, this action will not complete. A partial window appears but at this point it is basically the window of death. I have to reboot to recover my machine. This problem doesn't seem that uncommon. Based on my research most people seem to feel this has to do with threading. My program.cs has always had this attribute set. Here is my program.cs code.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
So is this just some kind of threading contention? Why does it work sometimes and not work other times?? Should the OpenFileDialogue be run in a background process perhaps?? I have a workaround for the issue while I debug (which is basically just paste in the file path), I just wanted to learn more about this behavior here if at all possible.

How to show/hide an application with Visible and ShowInTaskBar as false

How to show/hide an application that's running like:
Visible = false;
ShowInTaskBar = false;
using C#?
I tried unsuccessfully, using:
ShowWindow(handle, SW_SHOWNORMAL);
but it does not show it if the application is running in this above situation.
UPDATE; My scenery:
I have an application(written by me) that when WindowState is FormWindowState.Minimized I hide application of TaskBar and put it in "tray icon mode".
I'm using the following method for ensure application single instance:
[STAThread]
static void Main()
{
bool createdNew;
Mutex m = new Mutex(true, "...", out createdNew);
if (!createdNew)
{
Process currentProc = Process.GetCurrentProcess();
foreach (Process proc in Process.GetProcessesByName(currentProc.ProcessName))
{
if (proc.Id != currentProc.Id)
{
IntPtr handle = currentProc.Handle;
SetForegroundWindow(handle);
break;
}
}
}
else
{
Application.SetCompatibleTextRenderingDefault(false);
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}
the problem is,it works fine for ensure the single instance,but I want show application(exit of tray icon mode) if application is running.
I thought to do communication in applications,something like send message from app1(1) to app2, app2 read the message that's 1 and do some action. but I have no idea how do this, ShowWindow() seemed at ago hours the best way to do this,but #Hans Passant pointed some points,that it's not possible. I hope this is clear.
Different ways to solve this is very appreciated. Thanks again!
Changing the ShowInTaskbar property changes the Handle value. It is one of several Form class properties that can only be specified in the native CreateWindowEx() call and can't be changed later. So changing the property requires Winforms to re-create the window. And that gives it a different handle, making it very likely that the ShowWindow() call uses the wrong value.
You didn't find out that this was the problem because you are not checking the ShowWindow() return value. Very important when you pinvoke Windows calls, you don't have a friendly .NET exception to whack you over the head when the call failed.
I was looking for how to do this without single instance, and I just found the solution.
[STAThread]
static void Main()
{
string proc = Process.GetCurrentProcess().ProcessName;
Process[] processes = Process.GetProcessesByName(proc);
// No form has been created
if (processes.Length <= 1)
{
Form1 form = new Form1();
Application.Run(form);
}
// Form has been created
else
{
for (int i = 0; i < processes.Length; i++)
{
IntPtr formhwnd = FindWindow(null, "Form1");// get HWND by the text name of form
// Use WIN32 methods
int style = GetWindowLong(formhwnd, GWL_EXSTYLE);// Get EX-Style of the window
style |= WS_EX_APPWINDOW;// Add the APP style that shows icon in taskbar
style &= ~(WS_EX_TOOLWINDOW);// Delete the tool style that does not show icon in taskbar
SetWindowLong(formhwnd, GWL_EXSTYLE, style);// Set the EX-Style
ShowWindow(formhwnd, SW_SHOW);// Show the Window
SwitchToThisWindow(formhwnd, true);// Focus on the window
}
}
}
If you want to show/hide a window from another app. This can still be a reference.Just get the handle of that window and use the win32 methods (import from C++ dll) to set the window styles.

How to properly initialize a Windows Forms application

I have written this sample program - simplifying a very complex application. The same binary [.exe] throws a null pointer exception on some machines on startup. So, I want to know how to properly construct a Windows Forms form.
In our application the Form1_SizeChanged method is a result of this.ResumeLayout(false) method which is the last statement in InitializeComponents(). I don't know to simulate that, so I just changed the size myself for this test program.
public partial class Form1 : Form
{
public class Logger {
public Logger() { }
public void log(string str) {
Console.WriteLine("logging - " + str);
}
}
Logger logger = null;
public Form1()
{
InitializeComponent();
this.Size = new Size(200, 300);
MyInitialize();
}
private void MyInitialize() {
// Just that it takes some time.
Console.WriteLine("MyInitialize -- Enter");
for (int count = 0; count <5 ; count++){
Console.WriteLine("Sleeping -- " + count);
Thread.Sleep(1000);
}
logger = new Logger();
}
private void sleepingPill(int millisec) {
Thread.Sleep(millisec);
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
logger.log("Form1_SizeChanged -- Enter");
}
}
According to my understanding, Form1_SizeChanged should not be called unless Form1 is constructed properly. Can someone throw some light on how do the Windows Forms event architecture work in this scenario?
Original Stack Trace: from our complex application
System.NullReferenceException was unhandled
Message=Object reference not set to an instance of an object.
Source=ABCD
StackTrace:
at ABCD.Form1.AppendToLog(String s)
at ABCD.Form1.Form1_SizeChanged(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnSizeChanged(EventArgs e)
at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height, Int32 clientWidth, Int32 clientHeight)
at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height)
at System.Windows.Forms.Control.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
at System.Windows.Forms.Form.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
at System.Windows.Forms.Control.ScaleControl(SizeF factor, BoundsSpecified specified)
at System.Windows.Forms.ScrollableControl.ScaleControl(SizeF factor, BoundsSpecified specified)
at System.Windows.Forms.Form.ScaleControl(SizeF factor, BoundsSpecified specified)
at System.Windows.Forms.Control.ScaleControl(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
at System.Windows.Forms.ContainerControl.Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
at System.Windows.Forms.ContainerControl.PerformAutoScale(Boolean includedBounds, Boolean excludedBounds)
at System.Windows.Forms.ContainerControl.PerformNeededAutoScaleOnLayout()
at System.Windows.Forms.ContainerControl.OnLayoutResuming(Boolean performLayout)
at System.Windows.Forms.Control.ResumeLayout(Boolean performLayout)
at ABCD.Form1.InitializeComponent()
at ABCD.Form1..ctor()
at ABCD.Program.Main()
InnerException:
Notice from the stack trace: Form1_sizeChanged is called from InitializeComponents() .. which I think should not happen. Form1_sizeChanged is an instance method of Form1 class and should not be called before the Form1 is constructed. If the .NET environment want to process this event, it should wait till Form1 is constructed properly, shouldn't it?
In line two of the constructor, you set the Size. This will inturn call Form1_SizeChanged() before the logger is created. Move the setting of the Size after the call to MyInitialize.
Persumably, Form1_SizeChanged is being called before MyInitialize and hence before logger is initialised.
Change that Method to
if (logger != null)
logger.log("Form1_SizeChanged -- Enter");
I would guess that you are getting your null error on the SizeChanged event/
Your InitializeComponent() routine sets up the Form1_SizeChanged event to be mapped to the Form's SizeChanged event. Your this.Size code will fire said event. Since logger.log does not get initialized until your MyInitialize routine, you may see sporadic results.
Basically Windows will queue up the Form1_SizeChanged event and your app will respond to it asynchronously, so sometimes it might respond to it after the Logger() gets initialized and sometimes it will respond before the Logger gets initialized.
#Karephul: The reason you are seeing this behavior is that the message loop is at the application level, not the individual class level. A GUI window is a class like anything else and will receive events from the message loop as soon as the message loop can send them to the class and doesn't necessarily wait for the constructor to be finished.
You answered the question yourself in your question. It all has to do with the ResumeLayout() call.
The auto-generated "InitializeComponent()" code turns off layout, assembles the components, and then turns layout back on. This, I suppose, is an optimization because recalculating layout on every sub component addition could get slow. When layout gets turned back on, the resize events of lots of components will likely get called, including yours.
Note that the forms code plays fast and loose with what can get called from the constructor - calling all kinds of virtual methods that absolutely shouldn't get called until after the constructor is finished.
The fix is to initialize anything that must have a fully constructed object in the Load event. The Load event only gets called once and gets called after the instance is fully constructed. For you this means removing the Form1_SizeChanged event from what the development environment manages and add a Form1_Load event that includes
private void Form1_Load(object sender, EventArgs e)
{
this.Load += Form1_SizeChanged;
}
Yes it is a pain. Yes, you are right and Forms is wrong. At least the work around isn't too onerous.
Oh, I'm not done. I've heard that Move events can occur before the Load event but after the constructor is finished so if you need all Move events for some reason, you may need to just do defensive coding in the Move event handler like sgmoore showed.

A random cross-thread operation exception for Winforms multithreaded UI operation

For some reason, this safe-looking method raises a classic exception.
Cross-thread operation not valid:
Control 'statusLabel' accessed from a
thread other than the thread it was
created on.
This code obviously should call an anonymous method through Invoke when invoke is required.
But the exception occurs every once in a while.
Has anyone had a similar problem?
private void SetProgressBarValue(int progressPercentage)
{
Action setValue = () =>
{
var value = progressPercentage;
if (progressPercentage < 0)
value = 0;
else if (progressPercentage > 100)
value = 100;
statusProgressBar.Value = value;
statusLabel.Text = string.Format("{0}%", value);
};
if (InvokeRequired)
Invoke(setValue);
else
setValue();
}
Here is the stack-trace:
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
at System.Windows.Forms.ToolStrip.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
at System.Windows.Forms.ToolStrip.System.Windows.Forms.Layout.IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified)
at System.Windows.Forms.Layout.DefaultLayout.xLayoutDockedControl(IArrangedElement element, Rectangle newElementBounds, Boolean measureOnly, ref Size preferredSize, ref Rectangle remainingBounds)
at System.Windows.Forms.Layout.DefaultLayout.LayoutDockedControls(IArrangedElement container, Boolean measureOnly)
at System.Windows.Forms.Layout.DefaultLayout.xLayout(IArrangedElement container, Boolean measureOnly, ref Size preferredSize)
at System.Windows.Forms.Layout.DefaultLayout.LayoutCore(IArrangedElement container, LayoutEventArgs args)
at System.Windows.Forms.Layout.LayoutEngine.Layout(Object container, LayoutEventArgs layoutEventArgs)
at System.Windows.Forms.Control.OnLayout(LayoutEventArgs levent)
at System.Windows.Forms.ScrollableControl.OnLayout(LayoutEventArgs levent)
at System.Windows.Forms.Form.OnLayout(LayoutEventArgs levent)
at System.Windows.Forms.Control.PerformLayout(LayoutEventArgs args)
at System.Windows.Forms.Control.System.Windows.Forms.Layout.IArrangedElement.PerformLayout(IArrangedElement affectedElement, String affectedProperty)
at System.Windows.Forms.Layout.LayoutTransaction.DoLayout(IArrangedElement elementToLayout, IArrangedElement elementCausingLayout, String property)
at System.Windows.Forms.Control.PerformLayout(LayoutEventArgs args)
at System.Windows.Forms.Control.System.Windows.Forms.Layout.IArrangedElement.PerformLayout(IArrangedElement affectedElement, String affectedProperty)
at System.Windows.Forms.Layout.LayoutTransaction.DoLayout(IArrangedElement elementToLayout, IArrangedElement elementCausingLayout, String property)
at System.Windows.Forms.ToolStripItem.InvalidateItemLayout(String affectedProperty, Boolean invalidatePainting)
at System.Windows.Forms.ToolStripItem.OnTextChanged(EventArgs e)
at System.Windows.Forms.ToolStripItem.set_Text(String value)
at App.Image.Replace.ReplacementImageProcessForm.<>c__DisplayClass8.<SetProgressBarValue>b__7() in ReplacementImageProcessForm.cs: line 114
at App.Image.Replace.ReplacementImageProcessForm.SetProgressBarValue(Int32 progressPercentage) in ReplacementImageProcessForm.cs: line 119
at App.Image.Replace.ReplacementImageProcessForm.replacer_BeginReplace(Object sender, EventArgs e) in ReplacementImageProcessForm.cs: line 76
at App.Image.Replace.DocumentReplacer.OnBeginReplace() in IDocumentReplacer.cs: line 72
at App.Image.Replace.DocumentReplacer.Replace(Int32 documentId, String replacementDocumentPath) in IDocumentReplacer.cs: line 108
I still get the same error after implementing John Saunders's suggestion:
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
at System.Windows.Forms.ToolStrip.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
at System.Windows.Forms.ToolStrip.System.Windows.Forms.Layout.IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified)
at System.Windows.Forms.Layout.DefaultLayout.xLayoutDockedControl(IArrangedElement element, Rectangle newElementBounds, Boolean measureOnly, ref Size preferredSize, ref Rectangle remainingBounds)
at System.Windows.Forms.Layout.DefaultLayout.LayoutDockedControls(IArrangedElement container, Boolean measureOnly)
at System.Windows.Forms.Layout.DefaultLayout.xLayout(IArrangedElement container, Boolean measureOnly, ref Size preferredSize)
at System.Windows.Forms.Layout.DefaultLayout.LayoutCore(IArrangedElement container, LayoutEventArgs args)
at System.Windows.Forms.Layout.LayoutEngine.Layout(Object container, LayoutEventArgs layoutEventArgs)
at System.Windows.Forms.Control.OnLayout(LayoutEventArgs levent)
at System.Windows.Forms.ScrollableControl.OnLayout(LayoutEventArgs levent)
at System.Windows.Forms.Form.OnLayout(LayoutEventArgs levent)
at System.Windows.Forms.Control.PerformLayout(LayoutEventArgs args)
at System.Windows.Forms.Control.System.Windows.Forms.Layout.IArrangedElement.PerformLayout(IArrangedElement affectedElement, String affectedProperty)
at System.Windows.Forms.Layout.LayoutTransaction.DoLayout(IArrangedElement elementToLayout, IArrangedElement elementCausingLayout, String property)
at System.Windows.Forms.Control.PerformLayout(LayoutEventArgs args)
at System.Windows.Forms.Control.System.Windows.Forms.Layout.IArrangedElement.PerformLayout(IArrangedElement affectedElement, String affectedProperty)
at System.Windows.Forms.Layout.LayoutTransaction.DoLayout(IArrangedElement elementToLayout, IArrangedElement elementCausingLayout, String property)
at System.Windows.Forms.ToolStripItem.InvalidateItemLayout(String affectedProperty, Boolean invalidatePainting)
at System.Windows.Forms.ToolStripItem.OnTextChanged(EventArgs e)
at System.Windows.Forms.ToolStripItem.set_Text(String value)
at App.Image.Replace.ReplacementImageProcessForm.<>c__DisplayClassa.<>c__DisplayClassc.<SetProgressBarValue>b__9() in ReplacementImageProcessForm.cs: line 147
at App.Image.Replace.ReplacementImageProcessForm.InvokeIfNecessary(Control control, Action setValue) in ReplacementImageProcessForm.cs: line 156
at App.Image.Replace.ReplacementImageProcessForm.<>c__DisplayClassa.<SetProgressBarValue>b__7() in ReplacementImageProcessForm.cs: line 145
at App.Image.Replace.ReplacementImageProcessForm.InvokeIfNecessary(Control control, Action setValue) in ReplacementImageProcessForm.cs: line 156
at App.Image.Replace.ReplacementImageProcessForm.SetProgressBarValue(Int32 progressPercentage) in ReplacementImageProcessForm.cs: line 132
at App.Image.Replace.ReplacementImageProcessForm.replacer_BeginReplace(Object sender, EventArgs e) in ReplacementImageProcessForm.cs: line 74
at App.Image.Replace.DocumentReplacer.OnBeginReplace() in IDocumentReplacer.cs: line 87
at App.Image.Replace.DocumentReplacer.Replace(Int32 documentId, String replacementDocumentPath) in IDocumentReplacer.cs: line 123
This may or may not be directly relevant to your situation, but could provide a clue. One important leaky abstraction to remember about Windows Forms is that a window Handle is not created until it is actually required. The Handle property only creates the real Windows hwnd on the first get call, which doesn't occur when a Control-derived object (like a Windows Form) is instanced. (The Control-derived object, after all, is just a .NET class.) In other words, it's a property that is lazily initialized.
I've been burned by this before: The problem in my case is that I had correctly instanced a form on a UI thread, but I was not Show()ing it until the data had come back from a web service invocation that had been operating on a worker thread. The scenario was that no one had asked for the form's Handle, ever, until it was accessed as part of the InvokeRequired check that occurred when the worker thread had completed its work. So my background worker thread asked the form: do I need InvokeRequired? The InvokeRequired implementation of the form then said: well, let me look at my Handle so I can see what thread my internal hwnd was created on, and then I'll see if you're on that same thread. And so then the Handle implementation said: well I don't yet exist, so let me create an hwnd for myself right now. (You see where this is going. Remember, we're still on the background thread, innocently accessing the InvokeRequired property.)
This resulted in the Handle (and its underlying hwnd) being created on the worker thread, which I did not own, and which didn't have a message pump set up to handle Windows messages. The result: my app locked up when other calls were made to the previously hidden window, as these calls were made on the main UI thread, which reasonably assumed that all other Control-derived objects had also been created on this thread. In other cases, this could cause strange cross-thread exceptions because InvokeRequired would return false unexpectedly, since the Handle was created on a thread that is different from the thread that the form was instanced on.
But only sometimes. I had functionality whereby the user could cause the form to Show() itself via a menu, and then it would disable itself while it populated itself with data in the background (showing a throbber animation). If they did this first, then everything would be okay: the Handle was created on the UI thread (in the menu item's event handler), and so InvokeRequired behaved as expected when the worker thread finished retrieving data from the Web service. But if my background thread that periodically ran (it was an events scheduler, similar to the Event Reminder dialog in Outlook) accessed the Web service and tried to pop up the form, and the user hadn't yet Show()n it, then the worker thread's touching InvokeRequired would cause the heartburn-inducing behavior described above.
Good luck with your heisenbug!
Try overriding from Label, to create a new label class. Override the text property and place a breakpoint on it. Change the suspect label to use your new debug class instead. I've also found this technique is great for doing some basic profiling and/or debugging on your forms if you need to work out where and how things are being updated.
public class MyLabel : Label
{
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
}
Use your code and try and catch heisenbug, you'll be able to break on every access to the label, so if it comes from a stacktrace which you don't expect and/or isn't from your invoke code path, you have your bug?
Are you seeing this error when starting the application from the debugger, or when running stand-alone?
I have had the .NET Framework raise this exception incorrectly when running in the debugger. There is something peculiar about the debugger that causes the control's InvokeRequired flag incorrectly returns true, even when the code is running inside the main UI thread. It is very consitent for me, and it always happens after our control has been Disposed. Our stack trace looks like this:
System.InvalidOperationException: Cross-thread operation not valid: Control 'cboMyDropDown' accessed from a thread other than the thread it was created on.
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.TextBox.ResetAutoComplete(Boolean force)
at System.Windows.Forms.TextBox.Dispose(Boolean disposing)
at OurApp.Controls.OurDropDownControl.Dispose(Boolean disposing)
at System.ComponentModel.Component.Dispose()
You can see the source of the error from the .NET Framework source code:
public class Control //...
{ //...
public IntPtr Handle
{
get
{
if ((checkForIllegalCrossThreadCalls && !inCrossThreadSafeCall) && this.InvokeRequired)
{
throw new InvalidOperationException(System.Windows.Forms.SR.GetString("IllegalCrossThreadCall", new object[] { this.Name }));
}
if (!this.IsHandleCreated)
{
this.CreateHandle();
}
return this.HandleInternal;
}
}
}
When run in the debugger, checkForIllegalCrossThreadCalls is true, inCrossThreadSafeCall is false, and this.InvokeRequired is true despite being in the UI thread!
Note that Control.InvokeRequired winds up doing this:
int windowThreadProcessId = System.Windows.Forms.SafeNativeMethods.GetWindowThreadProcessId(ref2, out num);
int currentThreadId = System.Windows.Forms.SafeNativeMethods.GetCurrentThreadId();
return (windowThreadProcessId != currentThreadId);
Also note that our app uses the .NET Framework 2.0. Not sure if this is a problem in future versions, but I thought I'd write this answer anyway for posterity.
Try to use BeginInvoke() instead of Invoke().
To summarize, you have a private instance method SetProgressBarValue. It is an instance method of a control or form. This control or form contains other controls, statusProgressBar and statusLabel. So, you're doing the following, in effect:
if (this.InvokeRequired)
{
Invoke(
(Action) delegate
{
statusProgressBar.Value = 0; // TOUCH
statusLabel.Text = string.Format("{0}%", 0); // TOUCH
});
}
else
{
statusProgressBar.Value = 0; // TOUCH
statusLabel.Text = string.Format("{0}%", 0); // TOUCH
}
This code assumes that if this.InvokeRequired == false, that this implies statusProgressBar.InvokeRequired == false and statusLabel.InvokeRequired == false. I propose that you've found a situation where that is not true.
Try changing the code to:
private void SetProgressBarValue(int progressPercentage)
{
InvokeIfNecessary(
this, () =>
{
var value = progressPercentage;
if (progressPercentage < 0)
{
value = 0;
}
else if (progressPercentage > 100)
{
value = 100;
}
InvokeIfNecessary(
statusProgressBar.GetCurrentParent(),
() => statusProgressBar.Value = value);
InvokeIfNecessary(
statusLabel.GetCurrentParent(),
() =>
statusLabel.Text = string.Format("{0}%", value));
});
}
private static void InvokeIfNecessary(Control control, Action setValue)
{
if (control.InvokeRequired)
{
control.Invoke(setValue);
}
else
{
setValue();
}
}
I suspect you may somehow have caused the window handles of these three controls to be created on different threads. I think this code will work, even if all three window handles were created on different threads.
Nicholas Piasecki's answer sheds a lot of light on this problem for me. I've often had this odd bug and I appreciate the information as to why its occurring (Handle for a control probably being lazy-loaded on first call to this.InvokeRequired from a background thread)
Im creating a lot of UI dynamically (on the UI thread) and binding to presenters (MVP pattern) which often start worker threads before the UI has first show. There are of course updates to the UI and these are marshalled onto the UI thread using this.InvokeRequired/BeginInvoke, however at this point I assume a handle may be created on a worker thread.
For me the cross-thread violation is occurring in the MainForm dispose method, when the user closes the app. As a workaround I recursively iterate through child controls disposing of them and their children when the main form is closed. Then reducing the list of controls I disposed, I eventually narrowed it down to a single control that was causing the access violation. Unfortunately I wasn't able to solve the problem directly (calling CreateControl() or CreateHandle() on the control in question did nothing to resolve the problem), but I was able to work-around by leaving my recursive disposal in place on app shutdown.
Why this works and the built-in Form.Dispose() method doesn't I don't know.
Anyway I will be more careful in future when creating controls near worker threads, now I know the Handles are lazy-loaded, so thanks!
I had a similar problem where I was instantiating a form which launched a background thread to fetch some data and update itself before Show() had been called. On the second instance of this action(always), I would get a cross-thread exception on the Show().
After reading Nicholas' excellent answer, I put a breakpoint in the form's constructor and checked IsHandleCreated, which returned false. I then put in this code :
if (!this.IsHandleCreated)
this.CreateHandle();
I haven't seen the problem since. I know msdn recommends calling CreateControl instead of CreateHandle, however, CreateControl didn't cut it for me.
Does anyone know if there are any side-effects to calling CreateHandle directly?

Categories

Resources