3

I'm trying to do something very simple in principal, but I keep getting a cross-threading exception which has me stumped because I'm not setting out to use multiple threads.

I have a Windows Forms application. It launches another Windows Forms application (using the System.Diagnostics.Process class) , and catches the Exited event when that application is closed. My application event handler then tries to copy text from the clipboard to a control on the current displayed form. At this point a Cross-threading exception is thrown.

I assume that the problem is that the event from the closing application is in another thread (I'm outside my comfort zone here, so bear with me), so the question boils down to "How do I prevent this exception?"

I'm somewhat constrained into having to copy from the clipboard, but I could launch the other application a different way if that would solve the problem.

KF2
  • 9,887
  • 8
  • 44
  • 77
haughtonomous
  • 4,602
  • 11
  • 34
  • 52
  • 1
    How about some code? ;o) – DHN Dec 10 '12 at 16:58
  • @DHN This is a pretty straightforward problem with a very straightforward solution that won't really vary based on the specifics; I don't see how any extra code is needed. – Servy Dec 10 '12 at 17:03

1 Answers1

5

The Exited event doesn't fire in the UI thread, it fires in some background thread created by the Process class to monitor the other process.

You need to marshal to the UI thread to access controls, this can be done by using the Control.Invoke method:

textbox1.Invoke(new Action(()=> textbox1.Text = Process.ExitCode.ToString()));
Servy
  • 202,030
  • 26
  • 332
  • 449
  • I'm accessing the controls in the calling application. Is that what you mean? – haughtonomous Dec 10 '12 at 17:13
  • And the text I am assigning to the control comes from the clipboard - not the exited application itself. I don't understand why I am crossing threads. – haughtonomous Dec 10 '12 at 17:15
  • @haughtonomous The text assigned is irrelevant; I just choose something for the sake of the example. As I said in the answer, the cross thread exception is because the event is fired in another thread by the Process class. The fact that you don't explicitly create the new thread doesn't prevent the `Process` class from doing so internally. For an event handler the thread in which you attach the event is irrelevant, it fires it in whatever thread the class itself chooses to fire it in, and it didn't choose to use the UI thread. – Servy Dec 10 '12 at 17:20
  • Ok, I understand. Is there another way I could launch the slave application without creating another thread? In my main application there are encapsulation issues with legacy code which make your solution quite hard to implement in my case (the actual Forms controls are wrapped in other objects behind interfaces, etc etc). – haughtonomous Dec 10 '12 at 17:23
  • @haughtonomous Then you'll just end up moving this code further down the line. Instead of invoking to the UI thread in the event handler of the `Exited` event you'll need to invoke to the UI thread as soon as your interface chain reaches it's first UI element. I wouldn't be able to provide an example without knowing more. And no, there would not be any effective way to not start another thread. If you didn't start another thread you'd be blocking the UI thread, and your application would freeze until the process finished. The other thread is a necessity. – Servy Dec 10 '12 at 17:25
  • Ok. I think I will have to bite the bullet and modify the legacy code to ensure the thread marshalling is done there where the control property is assigned a value. Is it safe to do this (ie your suggestion) even if the property assignment is in the same thread (as it usually will be)? – haughtonomous Dec 10 '12 at 17:29
  • @haughtonomous Yes. If done excessively it may have a modest performance affect, but it won't throw an exception or anything like that. Also note that you don't need the specific `Textbox` to invoke, *any* control that you're using will work. Another option would be to add an `Invoke` method (with the same signature) to your interface, and call it on one of the Controls internally, thus exposing this (rather important functionally) externally, which may be appropriate. – Servy Dec 10 '12 at 17:29
  • There is also a way to only call invoke if necessary. This question talks about it a bit. It's basically what Servy said, but with something to check if "InvokeRequired" is true. http://stackoverflow.com/questions/2367718/c-automating-the-invokerequired-code-pattern . – Xantham Dec 10 '12 at 17:33
  • 1
    @Xantham `Invoke` will do the check internally anyway, so I have no idea why that construct is so common. It needlessly adds extra code for no additional benefit. – Servy Dec 10 '12 at 17:34
  • @Servy I was wondering if that was the case. It is what I learned first, so I thought it might be good to mention. Thanks for clearing that up for me as well. – Xantham Dec 10 '12 at 17:36