I imagined that WndProc is called on another thread rather than main UI thread.
I was right, so I thought that simply using InvokeRequired and Invoke on the form was enough to show a messagebox on the UI thread.
I was wrong. And I don't understand why.
How can I go around this problem?I'm looked around google but didn't find a solution.
What I'm trying to do is simply raising a custom event (ClipboardUpdate) when clipboard changes
the messagebox was just a test but didn't work, while just changing something like a string (a private field of the form) works, but it's not a good thing this behaviour because is a cross-thread operation in an unsafe way.
Update 1:
I don't have the code here because I created it on a friend's computer, however I can explain exactly what I wrote Because is short.
I created a basic winform with visual studio, without anything.
I used AddClipboardFormatListener (interop, but it's quite easy as a function, return int and accept IntPtr) function (on a windows 7 OS) to just detect WM_CLIPBOARDUPDATE message, inside winproc (a simple if, *if (e.Msg == ClipboardExtension.WM_CLIPBOARDUPDATE) DoClipboardUpdate();*).
Now the DoClipboardUpdate do this:
if (InvokeRequired)
Invoke(new VoidDelegate(OnClipboardUpdate));//Void delegate it's a delegate that doesn't take
// Params and returns void
else
OnClipboardUpdate();
Quite easy right? OnClipboardUpdate just do this:
if (ClipboardUpdate != null) ClipboardUpdate(null,EventArgs.Empty);
ClipboardUpdate is an event declared in this way:
public event EventHandler<EventArgs> ClipboardUpdate;
In the end, the only method subscrived to ClipboardUpdate event has this inside:
MessageBox.Show("test");
What happens when I run the code? The event is triggered (I tried with an exception and it works) and the messagebox doesn't popup, however I can't interact anymore with my form because it behaves as if a popup was opened (this is the "normal" behaviour when you open a popup on a different thread, that's why I said that).
Any suggestion on how to solve this?
I didn't understand why it happens but I created a new project and opening a messagebox from WndProc works fine, maybe some thread corrupted main thread memory, I don't know this but as others stated wndproc is the ui thread and should work
Related
So I am doing a somewhat lengthy progress in another class, and I want to give some progress info to my GUI. I am aware of background worker, and may use it for this if I HAVE to, but this operation is so simple that I feel that background worker is a bit more than I need. Instead, I am using eventhandlers to handle the updates, but the operation is inconsistent.
When a point has been reached in the worker class, it puts up an event telling about its progress. In the GUI class, I have an eventhandler listening for that event. When it finds it, it makes a string about it, and puts that string as the text of a label.
I then call labelname.Update() in that eventhandler, but nothing happens. Here's the really confusing part, I put a textbox there instead, set its text, and then called textboxname.Update(), and it worked. Why would .Update() not work for one control, but not another. Is there a trick to get it to work for a label?
I am turning Roken's comment into an answer, but his remark is the answer. You must implement the background operation in a background worker, period.
See this excellent article about the why and how.
(1) Try to call labelname.Invalidate(); before calling labelname.Update();
Calling the Invalidate method does not force a synchronous paint; to
force a synchronous paint, call the Update method after calling the
Invalidate method. (MSDN)
(2) Another option is calling labelname.Refresh();
Forces the control to invalidate its client area and immediately
redraw itself and any child controls. (MSDN)
I have a C# WinForms application that appears in the system tray. The application is hiding the main form on load:
private void MainForm_Load(object sender, System.EventArgs e)
{
Hide();
}
For the same main form, I've overriden the WndProc to catch a custom window message (that message is registered to Windows with the RegisterWindowMessage Win32 API call).
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYCUSTOM_MESSAGE)
{
// Handle custom message
}
}
From an external C++ application I'm broadcasting the same custom window message with PostMessage.
UINT msg = RegisterWindowMessage(L"WM_MYCUSTOM_MESSAGE");
PostMessage(HWND_BROADCAST, msg, NULL, NULL);
When I execute the above code after the C# application was first started, it doesn't get into the WndProc. After the main form is shown (by a double click on the system tray icon, which in essence does a Show()), catching the broadcast message works and keeps working after hiding the form with Hide().
Can anyone explain me why it doesn't work after the first Hide() from the MainForm_Load event handler? Am I hiding the form too early?
EDIT1: It seems like it has nothing to do with the Hide on load. Even without the initial Hide, my main form WndProc will only start accepting broadcast posts after it is hidden and re-shown...
After creating a small test application, I have found out that PostMessage() to HWND_BROADCAST doesn't arrive in Form.WndProc if Form.ShowInTaskbar is set to false while SendMessage() to HWND_BROADCAST does.
Even though the MSDN note about sending or posting messages to HWND_BROADCAST is exactly the same.
So it had nothing to do with the hiding or showing of the form and this seems like another undocumented feature of the Windows API.
There's something else going on, calling Hide() in the form's Load event handler doesn't actually hide the window. Try it with a little test Winforms app to see this.
If you don't actually see the window then the simple explanation is that the window just wasn't created. Which entirely explains why the message isn't being received. Override OnHandleCreated() and set a breakpoint on it to double-check this theory. The Show() method must be called to create the native window, apparently your NotifyIcon event handler is the first one to do so. Which happens when you call Application.Run() but don't pass a form instance.
Check this answer for a way to ensure that that a form is created but not made visible.
With Broadcast, the message is posted to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows. The message is not posted to child windows (according to MSDN). Use an application, like WinSpy to make sure your windows is a top level one right after the initial startup and hiding. Also this http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/ might (or might not) give some insights on how the OnLoad works... Seems it's a little bit more complicated. Try to hide the MainForm somewhere else, not on the OnLoad.
I have a problem that I have found many similar problems related to multi threading, but noone linked to me specific problem.
I have a method that does something, but when the method has been called, I don't want it to return untill a button is clicked.
So basically, at the end of the method, I would like it to stay put and wait for the Button_Click event to finish, and then the event should in some way "tell" the method to continue.
Currently I have done this by adding a loop at the end of the method like this:
while(someVariable){
Thread.Sleep(10);
Application.DoEvents();
}
Then the Button_Click event set someVariable to be false, and then the loop stops.
Of course, this looks very "iffy", and having this loop run every 10 milliseconds seems like a gigantic waste.
Are there any way of doing this properly? Since the method in question runs on the same thread as the rest of the application, it's also important that halting the method does not block the tread for other activities.
The reason I need this is because when the method is called from somewhere else, this method will draw up some components (including two buttons). Then the user clicks one of them, and then the method will return a different value depending on which button was clicked, and the program calling it can't continue untill it knows which button was clicked. So my program will look like this.
....
if( someMethod() == ButtonA ){
//do the proper action if button 1 is clicked
}else{
//do the proper action if button 2 is clicked
}
I hope this was not to confusing.
Assuming this is in the UI thread, you absolutely should not do this. You say "it's important that halting the method does not block the thread for other activities" - that basically means you need to find a different design. If the UI thread is "staying put and waiting for the Button_Click event to finish" then it can't be doing anything else without using the hack of Application.DoEvents().
Instead, you should make a call back to the original caller when the event occurs. Either expose an event that the caller can subscribe to, and raise that event when the button click finishes, or get the caller to pass in a delegate to call appropriately. (They're equivalent really, just different ways of propagating the callback.)
That's the way WinForms (and other rich client UIs) are designed to work - on an event-based model, basically.
Use AutoResetEvent, see sample and documentation at http://msdn.microsoft.com/en-us/library/58195swd.aspx
Your main thread does a WaitOne() on the event and in you Button.Click event, you Set() the event.
I have a C# windows forms application. The way I currently have it set up, when Form1_Load() runs it checks for recovered unsaved data and if it finds some it prompts the user if they want to open that data. When the program runs it works alright but the message box is shown right away and the main program form (Form1) does not show until after the user clicks yes or no. I would like the Form1 to pop up first and then the message box prompt.
Now to get around this problem before I have created a timer in my Form, started the timer in the Form1_Load() method, and then performed the check and user prompt in the first Timer Tick Event. This technique solves the problem but is seems like there might be a better way.
Do you guys have any better ideas?
Edit: I think I have also used a background worker to do something similar. It just seems kinda goofy to go through all the trouble of invoking the method to back to the form thread and all that crap just to have it delayed a couple milliseconds!
I would use Form1_Shown()
Use the Shown event. It seems to suit what you need, and will only display the first time the form is shown.
Form f1 = new Form();
f1.Shown += new EventHandler(f1_Shown);
public void f1_Shown(object sender, EventArgs e)
{
// Show dialog in here
}
Try the "Shown" event:
Form.Show Event
Using a Windows.Forms.Timer is a good, stable, well-known, and easily understood technique for doing what you want. I would avoid any other timer objects.
The form's Shown event works well.
Overload / override the Show method. (My preferred technique for greater control.) In this method, I would do the checking needed. When ready, I would call the base.Show method, then do any other processing, such as message boxes, prompts, logging, or whatever.
I have a simple UserControl for database paging, that uses a controller to perform the actual DAL calls. I use a BackgroundWorker to perform the heavy lifting, and on the OnWorkCompleted event I re-enable some buttons, change a TextBox.Text property and raise an event for the parent form.
Form A holds my UserControl. When I click on some button that opens form B, even if I don't do anything "there" and just close it, and try to bring in the next page from my database, the OnWorkCompleted gets called on the worker thread (and not my Main thread), and throws a cross-thread exception.
At the moment I added a check for InvokeRequired at the handler there, but isn't the whole point of OnWorkCompleted is to be called on the Main thread? Why wouldn't it work as expected?
EDIT:
I have managed to narrow down the problem to arcgis and BackgroundWorker. I have the following solution wich adds a Command to arcmap, that opens a simple Form1 with two buttons.
The first button runs a BackgroundWorker that sleeps for 500ms and updates a counter.
In the RunWorkerCompleted method it checks for InvokeRequired, and updates the title to show whethever the method was originaly running inside the main thread or the worker thread.
The second button just opens Form2, which contains nothing.
At first, all the calls to RunWorkerCompletedare are made inside the main thread (As expected - thats the whold point of the RunWorkerComplete method, At least by what I understand from the MSDN on BackgroundWorker)
After opening and closing Form2, the RunWorkerCompleted is always being called on the worker thread. I want to add that I can just leave this solution to the problem as is (check for InvokeRequired in the RunWorkerCompleted method), but I want to understand why it is happening against my expectations. In my "real" code I'd like to always know that the RunWorkerCompleted method is being called on the main thread.
I managed to pin point the problem at the form.Show(); command in my BackgroundTesterBtn - if I use ShowDialog() instead, I get no problem (RunWorkerCompleted always runs on the main thread). I do need to use Show() in my ArcMap project, so that the user will not be bound to the form.
I also tried to reproduce the bug on a normal WinForms project. I added a simple project that just opens the first form without ArcMap, but in that case I couldn't reproduce the bug - the RunWorkerCompleted ran on the main thread, whether I used Show() or ShowDialog(), before and after opening Form2. I tried adding a third form to act as a main form before my Form1, but it didn't change the outcome.
Here is my simple sln (VS2005sp1) - it requires
ESRI.ArcGIS.ADF(9.2.4.1420)
ESRI.ArcGIS.ArcMapUI(9.2.3.1380)
ESRI.ArcGIS.SystemUI (9.2.3.1380)
Isn't the whole point of OnWorkCompleted is to be called on the Main thread? Why wouldn't it work as expected?
No, it's not.
You can't just go running any old thing on any old thread. Threads are not polite objects that you can simply say "run this, please".
A better mental model of a thread is a freight train. Once it's going, it's off on it's own track. You can't change it's course or stop it. If you want to influence it, you either have to wait til it gets to the next train station (eg: have it manually check for some events), or derail it (Thread.Abort and CrossThread exceptions have much the same consequences as derailing a train... beware!).
Winforms controls sort of support this behaviour (They have Control.BeginInvoke which lets you run any function on the UI thread), but that only works because they have a special hook into the windows UI message pump and write some special handlers. To go with the above analogy, their train checks in at the station and looks for new directions periodically, and you can use that facility to post it your own directions.
The BackgroundWorker is designed to be general purpose (it can't be tied to the windows GUI) so it can't use the windows Control.BeginInvoke features. It has to assume that your main thread is an unstoppable 'train' doing it's own thing, so the completed event has to run in the worker thread or not at all.
However, as you're using winforms, in your OnWorkCompleted handler, you can get the Window to execute another callback using the BeginInvoke functionality I mentioned above. Like this:
// Assume we're running in a windows forms button click so we have access to the
// form object in the "this" variable.
void OnButton_Click(object sender, EventArgs e )
var b = new BackgroundWorker();
b.DoWork += ... blah blah
// attach an anonymous function to the completed event.
// when this function fires in the worker thread, it will ask the form (this)
// to execute the WorkCompleteCallback on the UI thread.
// when the form has some spare time, it will run your function, and
// you can do all the stuff that you want
b.RunWorkerCompleted += (s, e) { this.BeginInvoke(WorkCompleteCallback); }
b.RunWorkerAsync(); // GO!
}
void WorkCompleteCallback()
{
Button.Enabled = false;
//other stuff that only works in the UI thread
}
Also, don't forget this:
Your RunWorkerCompleted event handler should always check the Error and Cancelled properties before accessing the Result property. If an exception was raised or if the operation was canceled, accessing the Result property raises an exception.
It looks like a bug:
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=116930
http://thedatafarm.com/devlifeblog/archive/2005/12/21/39532.aspx
So I suggest using the bullet-proof (pseudocode):
if(control.InvokeRequired)
control.Invoke(Action);
else
Action()
The BackgroundWorker checks whether the delegate instance, points to a class which supports the interface ISynchronizeInvoke. Your DAL layer probably does not implement that interface. Normally, you would use the BackgroundWorker on a Form, which does support that interface.
In case you want to use the BackgroundWorker from the DAL layer and want to update the UI from there, you have three options:
you'd stay calling the Invoke method
implement the interface ISynchronizeInvoke on the DAL class, and redirect the calls manually (it's only three methods and a property)
before invoking the BackgroundWorker (so, on the UI thread), to call SynchronizationContext.Current and to save the content instance in an instance variable. The SynchronizationContext will then give you the Send method, which will exactly do what Invoke does.
The best approach to avoid issues with cross-threading in GUI is to use SynchronizationContext.