.NET Forum / .NET Framework / CLR / April 2008
Relationship between Application.Exit() and AppDomain
|
|
Thread rating:  |
Sunny S - 04 Apr 2008 10:25 GMT Hi,
Does Application.Exit() apply only to the AppDomain in which it is called? Is it the same as in WPF – a single Application object per AppDomain? Generally speaking, what is the relationship between an Application object and AppDomain objects?
Regards,
Sunny S.
Scott M. - 04 Apr 2008 15:59 GMT One AppDomain per application. One or more AppDomains in a Process.
WPF doesn't change this as WPF is still a .NET application and is still run by the CLR.
Application.Exit will kill the application you are calling it on and therefore the AppDomain it is running in.
> Hi, > [quoted text clipped - 6 lines] > > Sunny S. Jon Skeet [C# MVP] - 04 Apr 2008 16:16 GMT > One AppDomain per application. One or more AppDomains in a Process. > [quoted text clipped - 3 lines] > Application.Exit will kill the application you are calling it on and > therefore the AppDomain it is running in. As far as I'm aware, Application.Exit just stops the UI message loop on the current thread. It doesn't try to do anything to the AppDomain.
A quick experiment *appears* to show that you can't run a WinForms message loop on the same thread twice, but I could just be missing something. If you create a new thread for each one though, you can call Application.Exit in the same AppDomain multiple times perfectly reasonably:
using System; using System.Drawing; using System.Windows.Forms; using System.Threading;
public class Test { static void Main() { for (int i=0; i < 5; i++) { Thread t = new Thread(ShowUI); t.SetApartmentState(ApartmentState.STA); t.Start(i); t.Join(); } }
static void ShowUI(object state) { Form form = new Form { Size = new Size(200, 200), Text = "Form #"+state }; Button b = new Button { Text = "Die!", Location = new Point(5, 5) }; b.Click += (s, e) => { Application.Exit(); }; form.Controls.Add(b); Application.Run(form); } }
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet World class .NET training in the UK: http://iterativetraining.co.uk
Sunny S - 10 Apr 2008 18:45 GMT Hi Jon,
I modified your example code to create two threads and to start message loops on each of them as follows:
static void Main() { { Thread t = new Thread(ShowUI1); t.SetApartmentState(ApartmentState.STA); t.Start();
Thread t2 = new Thread(ShowUI2); t2.SetApartmentState(ApartmentState.STA); t2.Start(); } }
static void ShowUI1() { Form form = new Form { Size = new Size(200, 200), Text = "Form UI1" }; Button b = new Button { Text = "Die!", Location = new Point(5, 5) }; b.Click += (s, e) => { Application.Exit(); }; form.Controls.Add(b);
Application.Run(form); }
static void ShowUI2() {
Form form = new Form { Size = new Size(200, 200), Text = "Form UI2" }; Button b = new Button { Text = "Die!", Location = new Point(5, 5) }; b.Click += (s, e) => { Application.Exit(); }; form.Controls.Add(b);
Application.Run(form); }
If you run this code, you’ll see that when you kill one of the Forms the other dies too and the application shuts down. Apparently, calling Application.Exit() on one thread kills the other thread's UI as well. This is in line with Microsoft documentation saying that Application.Exit() method ‘Informs all message pumps that they must terminate, and then closes all application windows after the messages have been processed’.
Things seem to be different if you create these two threads in different AppDomains like so:
public class Test: MarshalByRefObject { static void Main() { { AppDomain ad1 = AppDomain.CreateDomain("AD1"); Test t1 = (Test) ad1.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "Test"); t1.StartUI1();
AppDomain ad2 = AppDomain.CreateDomain("AD2"); Test t2 = (Test)ad2.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "Test"); t2.StartUI2();
} }
public void StartUI1() { Thread t = new Thread(ShowUI1); t.SetApartmentState(ApartmentState.STA); t.Start(); }
public void StartUI2() { Thread t = new Thread(ShowUI2); t.SetApartmentState(ApartmentState.STA); t.Start(); }
public void ShowUI1() { Form form = new Form { Size = new Size(200, 200), Text = "Form UI1" }; Button b = new Button { Text = "Die!", Location = new Point(5, 5) }; b.Click += (s, e) => { Application.Exit(); }; form.Controls.Add(b);
Application.Run(form); }
public void ShowUI2() {
Form form = new Form { Size = new Size(200, 200), Text = "Form UI2" }; Button b = new Button { Text = "Die!", Location = new Point(5, 5) }; b.Click += (s, e) => { Application.Exit(); }; form.Controls.Add(b);
Application.Run(form); } }
In this case killing one of the Forms doesn’t kill the other one and this is not in line with the documentation.
That’s, essentially, what my question was about.
Thanks for replying, it’s nice to hear from you again:)
SS
Sunny S - 10 Apr 2008 19:01 GMT Jon,
I’ve just done some more tests. My understanding of Willy Denoyette’s explanation (see at the bottom of this thread) was that Application.Exit() kills the message pumps in the current Appdomain and its ‘child’ AppDomains.
To verify this I further modified the code with AppDomains so that one of the threads creates yet another ‘child’ AppDomain and starts yet another message loop in it. This test showed that my understanding was incorrect. All three message loops in those AppDomains are independent in the sense that killing one of them doesn’t kill the others.
Willy Denoyette [MVP] - 10 Apr 2008 21:40 GMT > Jon, > [quoted text clipped - 9 lines] > three message loops in those AppDomains are independent in the sense that > killing one of them doesn’t kill the others. I know this is quite confusing, but I'm not sure why you consider this "not inline with the docs". Note that the docs talks about applications, and stopping all message pumps in the application, it doesn't talk about threads running other AD's.
In your last sample you are creating separate AD/thread pairs *and* you create a new application (Application.Run(..) in each of the domains/threads, calling A.E in one of the AD/threads only stops the (single) message pumps in this application, which is inline with the docs, right?
Now, say the from AD1/thread1 you create another thread, and you create a window and run a message pump in this thread. That means, you have AD1/thread1 and thread2, and two separate message pumps. Calling A.E from one of these threads will stop all (2) pumps, which is inline with the docs, right?
And, say you have a AD/thread that runs an application (Application.Run(...)], and this thread creates another AD that shows a form. Here you have two AD's, one *Application* and only one thread that pumps the message queue of both the main form and the other AD's form. When you call A.E from the second form code, you will simply unload the second AD and it's form, but you won't stop the message pump. Calling A.E from the main form will stop the message pump and close all forms tear down the AD's for the whole application, which is again inline with the doc's.
Willy.
Sunny S - 11 Apr 2008 08:52 GMT Hi Willi,
I agree that 'not in line with the docs' was, perhaps, an overstatement:). What I actually wanted to say is that as far as I am concerned the documentation was not clear about the scope of the Application object and the relationship between Application, A.E method and AppDomains.
For example, you say ' ... you create a new application (Application.Run(..) in each of the domains/threads ...' Define 'application'. Surely it's not a new Application object or is it? BTW, the documentation just says that calling A.R 'Begins running a standard application message loop on the current thread'. I am not trying to be argumentative, I'm just trying to understand what's going on.
Let's take one of your scenarios: "From AD1/thread1 you create another thread, and you create a window and run a message pump in this thread." Why can't I use the same logic as above and say that by 'creating a window and running a message pump' on a new thread (through Application.Run(Form), I presume) I create a new application?
Let's futher modify this scenario so that the second thread is created, as above, from AD1/thread1, but in a separate AppDomain (AD2), so that AD2 is a 'child' of AD1. In this case, calling A.E on either of the threads doesn't stop the other (I did test it:).
You see, this is the sort of questions I was trying to clarify for myself and I found your comments quite helpful, thank you.
> > Jon, > > [quoted text clipped - 38 lines] > Willy. > Willy Denoyette [MVP] - 11 Apr 2008 12:03 GMT See inline.
WIlly.
> Hi Willi, > [quoted text clipped - 3 lines] > the > relationship between Application, A.E method and AppDomains. Well, as said before, Windows.Forms was designed to be used from the "default domain" only and not from muttiple AD's running in the same process. The reason is quite simple, the underlying Win32 Windows architecture doesn't know about AD's, it only knows about processes and threads and one thread which is important in the "Windows" context is the UI thread. It's the thread that must run a message pump in order to retrieve Window messages from the application and from the System.
> For example, you say ' ... you create a new application > (Application.Run(..) [quoted text clipped - 4 lines] > current thread'. I am not trying to be argumentative, I'm just trying to > understand what's going on. A.R creates an ApplicationContext (and a static Application instance ), it's the ApplicationContext who actually defines *the* application in terms of the MSDN docs.
> Let's take one of your scenarios: "From AD1/thread1 you create another > thread, and you create a window and run a message pump in this thread." > Why > can't I use the same logic as above and say that by 'creating a window and > running a message pump' on a new thread (through Application.Run(Form), I > presume) I create a new application? You create a new Application in terms of the docs, that's right.
> Let's futher modify this scenario so that the second thread is created, as > above, from AD1/thread1, but in a separate AppDomain (AD2), so that AD2 is > a > 'child' of AD1. In this case, calling A.E on either of the threads doesn't > stop the other (I did test it:). Ok so you have: AD1/T1 -> creates T2 -> creates AD2 -> call A.R which creates a new ApplicationContext (a new application as per msdn) which start a pump on T2. Calling A.E in T2 will stop the pump on T2, destroy the ApplicationContext and as such stop this application. The fact that you do this is a new AD is irrelevant, an AD != an "application".
> You see, this is the sort of questions I was trying to clarify for myself > and I found your comments quite helpful, thank you. [quoted text clipped - 52 lines] >> >> Willy. Sunny S - 11 Apr 2008 12:45 GMT Hi Willi,
We are almost there:):):)
To recap, we have: AD1/T1 -> calls A.R(Form1); creates T2 -> creates AD2 -> call A.R(Form2). Here calling A.E on T2 does close Form2, but doesn't close Form1. The reverse is also true: calling A.E on T1 closes Form1, but doesn't close Form2.
I accept your explanation that A.R(Form2) creates a new 'application' that is independent of the application running Form1, so exiting one of them doesn't affect the other.
Why then things are different for the following scenario: AD1/T1 -> calls A.R(Form1); creates T2 -> calls A.R(Form2), where we don't create a separate AppDomain for T2? As you pointed out erlier, closing either Form1 or Form2 closes the other Form and shuts down the application.
Apparently, calling A.R on a different thread in a separate AppDomain is somehow different from calling it in the same AppDomain. Would it be correct to assume that the scope of Application.Exit() is limited to the current AppDomain only?
> See inline. > [quoted text clipped - 108 lines] > >> > >> Willy. Willy Denoyette [MVP] - 11 Apr 2008 15:50 GMT > Hi Willi, > [quoted text clipped - 7 lines] > doesn't > close Form2. Yep.
> I accept your explanation that A.R(Form2) creates a new 'application' > that [quoted text clipped - 6 lines] > AppDomain for T2? As you pointed out erlier, closing either Form1 or Form2 > closes the other Form and shuts down the application. Here you create one AD, 2 threads, 2 applications (ApplicationContexts, ApplicationThreadContexts, etc... ) , 2 message pumps (one per thread). When you call A.E in one of the threads, you get rid of all of the ApllicationContexts in the containing AD and it's AD children. Note that what's happening is quite more complex, but that's the big picture.
Why are you so interested in A.E's behavior, which is not the most appropriate way to stop an application anyway, just currious.
> Apparently, calling A.R on a different thread in a separate AppDomain is > somehow different from calling it in the same AppDomain. Would it be [quoted text clipped - 137 lines] >> >> >> >> Willy. Sunny S - 11 Apr 2008 17:18 GMT Hi Willy,
Well, what can I say? A big thank you, again:)
As for your question why am I so interested in A.E -- at this point it's already, perhaps, a bit of an obsession. My original interest was triggered by attempts to find a way to rudely shut down an application that is running in an AppDomain that was created by a host. The app is supposed to be written by a third party, should be considered as an untrusted one and peppered with bugs (that is, in addition to our own bugs:)). If that app enters a tight loop, then I couldn’t see a way to quit it gracefully other than calling A.E. Unloading an AppDomain, even if that was possible with a foreground thread still running in it, wouldn’t be a good option since we would loose state information. So A.E seemed to be a reasonable option. So I started thinking of what would happen if I inject a thread into that problematic AppDomain and call A.E on it, and then I came up with some other scenarios most of which included A.E in one way or another. Obviously, I needed to understand what are possible ramifications, etc. BTW, where and how can I learn what happens ‘under the hood’? I tried Reflector, but the most interesting (and messy?) parts of functionality are hidden in internal methods which Reflector wouldn’t ‘reflect’:)). I don't want to be too intrusive, but how did you learn what's going on behind 'the big picture'?
Regards,
SS
Jeffrey Tan[MSFT] - 12 Apr 2008 09:26 GMT Hi Sunny,
Sorry for the late response to you, I am out of office yesterday. Anyway, I am glad Willy has provided a great and informative discussion with you.
I assume your application is coded in Winform since you are using System.Winforms.Forms.Application. So, your application created a seperate AppDomain to host the 3rd party code for security/robustness reason ? Can you tell us if the 3rd party code is also GUI code? This design and achitecture looks strange to me, since I did not see any programs using this design. We normally run the semi-trusted non-GUI addin in the separate AppDomain.
I ask these because Application.Exit can only be used in GUI Winform code not normal non-GUI code. Also, it can only shut down the GUI threads in the *Application*. If the 3rd party code created a few more worker(non-GUI) threads, they will be cleaned by Application.Exit. As Willy originally pointed out, Application.Exit() has nothing to do with AppDomain, so it will not help to clearn AppDomain. Why don't you call AppDomain.Unload method to get rid of the buggy 3rd party code?
Regarding your last question, I think Reflector should be the best tool to understand the .Net BCL internal work. What internal call functions do you have problem to understand? As I know Winform purely encapsulates the Win32 GUI code, so I assume most of the non-.Net code is p/invoking the Win32 User32 APIs. The Win32 GUI programming books(such as <Programming Windows>) should be good resource to help you understand GUI APIs.
Hope this helps.
Best regards, Jeffrey Tan Microsoft Online Community Support ========================================= Delighting our customers is our #1 priority. We welcome your comments and suggestions about how we can improve the support we provide to you. Please feel free to let my manager know what you think of the level of service provided. You can send feedback directly to my manager at: msdnmg@microsoft.com.
This posting is provided "AS IS" with no warranties, and confers no rights.
Sunny S - 12 Apr 2008 15:01 GMT Hi Jeff,
Thanks for replying and for your comments. To answer your questions,
1. Yes, the app we are writing is being coded in Winform, it's not a Web application, at least for the time being.
2. I can see two explanations for our design being strange to you:) - a) we are doing something genuinely stupid; b) we are doing something genuinely new (a combination of both is also an option:):):)). On a serious note, we are trying to create an app that would host some other applications, either GUI or non-GUI, which would be configurable through the host UI. The host is supposed to be non-intrusive; once the apps are configured they should run as there is no host, although they would be required to respond to host’s request about their status.
3. Why don’t I call AppDomain.Unload()? I actually do, but there are scenarios when I don’t want or cannot do this. (a) If the 3rd party app that sits in this AppDomain is running a foreground thread then AppDomain.Unload throws an exception. (b) If there is a problem with a 3rd party app, so that it cannot continue, we would like to stop it - not in a graceful way, perhaps - and grab some status data. Then we can unload the whole thing. (c) We should be able to stop the 3rd party app, reconfigure it and start again.
4. Just to illustrate what I was talking about when I said that there were limits to which Reflector could ‘reflect’, let’s take the Object class. Look, for example, at
internal static extern bool InternalEquals(object objA, object objB)
As far as I know, there is no matching Win32 API to p/invoke and this is as far as I can go in exploring the CLR internals.
Anyway, thank you very much for your help.
SS
> Hi Sunny, > [quoted text clipped - 37 lines] > > This posting is provided "AS IS" with no warranties, and confers no rights. Jeffrey Tan[MSFT] - 14 Apr 2008 10:17 GMT Hi Sunny,
Thanks for your feedback.
Regarding "(a) If the 3rd party app that sits in this AppDomain is running a foreground thread then AppDomain.Unload throws an exception", do you mean ThreadAbortException thrown in the thread in the 3rd party AppDomain? Yes, AppDomain.Unload() method uses Thread.Abort() method on the target thread to terminate the threads. ThreadAbortException will perform stack unwind on the target stack to perform clean-up and then terminate the target thread. Then, what is the problem of this behavior?
Regarding (b) and (c), I also do not think I understand the key points. What is the problem of AppDomain.Unload() against your (b) and (c) requirement?
Regarding the Reflector issue, it seems that you are interested in the internal routines implemented by the CLR. Yes, a lot of .Net services are implemented in the CLR via native code. They are using "FCall" into the CLR which can not be examined using Reflector. The only way to examine its implementation is reversing engineer(disassemble) it. Also, Microsoft released an open-source CLR implementation called SSCLI, which contains an example implementation of CLR and .Net Framework. You may examine its source code to understand these FCall functions. The SSCLI source code is not guaranteed to be the same as CLR, but most of the principles are the same. The articles below also talks about FCall: http://blogs.msdn.com/yunjin/archive/2004/02/08/69906.aspx https://blogs.msdn.com/joelpob/archive/2003/12/18/53745.aspx
Also, a lot of interesting books talk about CLR internal implementation, such as Jeffrey Richter's <CLR via C#>, Don Box's <Essential .Net>, <Shared Source CLI Essentials> etc.. They are helpful to understand CLR principles.
Hope this helps.
Best regards, Jeffrey Tan Microsoft Online Community Support ========================================= Delighting our customers is our #1 priority. We welcome your comments and suggestions about how we can improve the support we provide to you. Please feel free to let my manager know what you think of the level of service provided. You can send feedback directly to my manager at: msdnmg@microsoft.com.
This posting is provided "AS IS" with no warranties, and confers no rights.
Sunny S - 14 Apr 2008 13:27 GMT Hi Jeff,
Thanks for replying and my special thanks for the link to FCall. This should be very useful (or at least very interesting:) ).
Regarding (a) in my previous post: The exception that AppDomain throws is actually CannotUnloadAppDomainException and VS2008 gives a hint: 'Make sure you are not trying to unload an application domain that ... has a running thread ...'. Again, this is only a problem for foreground threads.
Regarding (b): basically the idea is to stop all processing, create some types defined in the loaded assemblies, and call some methods on those types. It may not be always possible if one of the types is running a tight loop on a foreground thread. So the first thing to do is to try and kill all the threads.
Regarding (c): The host we are writing should be able to swap an assembly that is suspected to cause problems with an alternative one. Of course, this is also possible by unloading the AppDomain and loading it with the same assemblies except the suspected one. However, in our case under certain scenarios this could be time consuming and as an alternative we are investigating the possibility of swapping only the suspected assembly without unloading the whole set.
Books recommendations: I've already read Jeff Richter’s <CLR via C#> (both editions) and right now I am finishing <Customizing The Microsoft .Net Framework CLR> by Steve Pratschner. <Shared Source .. > seems to be interesting, thanks, I’ll have a look.
Again, thank you very much for your help. Where do you guys find time to answer all our questions?:) You seem to be so willing to help; that’s quite impressive..
Regards,
SS
> Hi Sunny, > [quoted text clipped - 42 lines] > > This posting is provided "AS IS" with no warranties, and confers no rights. Jeffrey Tan[MSFT] - 15 Apr 2008 09:12 GMT Hi Sunny,
Thank you for explaining it in details.
Oh, I was not aware of that the foreground UI thread will block the AppDomain.Unload() method with CannotUnloadAppDomainException. Have you invested to find out why the Thread.Abort did not terminate the target thread? I have had a hard time to understand why the thread did not terminate.
Anyway, if Application.Exit() can terminate all the foreground GUI threads in the target AppDomain, I think we can call Application.Exit() to terminate all the UI threads in the target AppDomain and followed with an AppDomain.Unload() method calling to clean up the target AppDomain.
<Shared Source CLI Essentials> explains the CLI(which is very similiar with CLR) in source-code level, so it has a deeper insight than other CLR books. <Customizing The Microsoft .Net Framework CLR> targets the CLR hosting topics in depth.
Actually, I am the .Net CLR and VS.net debugging newsgroups MSDN subscription support engineer, so it's my job and pleasure to help you
:-).(Also, I learned a lot in the newsgroup discussion) Best regards, Jeffrey Tan Microsoft Online Community Support ========================================= Delighting our customers is our #1 priority. We welcome your comments and suggestions about how we can improve the support we provide to you. Please feel free to let my manager know what you think of the level of service provided. You can send feedback directly to my manager at: msdnmg@microsoft.com.
This posting is provided "AS IS" with no warranties, and confers no rights.
Sunny S - 15 Apr 2008 10:43 GMT Hi Jeff,
Thanks for replying. To answer your question whether I tried to find out why Thread.Abort() didn’t terminate the foreground thread, - no, I didn’t even try. First, I am lazy and I prefer to ask experts; this does save time in the short run:) Second, the documentation on Thread.Abort says ‘The thread is not guaranteed to abort immediately, or at all’ and that is enough for me (see the first reason:))).
In my simplistic scenario the thread was doing something like this: while (true) {}; so given the information I found at
http://www.ondotnet.com/pub/a/dotnet/2003/02/18/threadabort.html
I assumed there was no chance for that sort of thread to be aborted. Then I asked myself a question why is it still aborted when it is created as a background thread? I figured that trying to find an answer to this question would take me an unjustified amount of time given the fact that stopping the thread by Application.Exit() gives an immediate practical solution. I do need to read the books you’ve recommended …
Regards, SS
> Hi Sunny, > [quoted text clipped - 31 lines] > > This posting is provided "AS IS" with no warranties, and confers no rights. Willy Denoyette [MVP] - 15 Apr 2008 09:28 GMT > Hi Jeff, > [quoted text clipped - 96 lines] >> This posting is provided "AS IS" with no warranties, and confers no >> rights. I agree with Jeffrey here, AppDomain.Unload() can/should not be blocked by a running/blocked UI thread, in those cases where Thread.Abort did not terminate the thread, the CLR would escalate to a rude abort which cannot be blocked anyway.
Willy.
Sunny S - 15 Apr 2008 19:21 GMT > I agree with Jeffrey here, AppDomain.Unload() can/should not be blocked by a > running/blocked UI thread, in those cases where Thread.Abort did not > terminate the thread, the CLR would escalate to a rude abort which cannot be > blocked anyway. > > Willy. Hi Willy,
Thanks for replying. I actually wasn’t talking about a UI thread, if I understood your comment correctly. To avoid confusion, I’ve appended the code that illustrates my point.
The code creates two forms, UI1 and UI2. Hit the ‘Start Thread’ button on UI2 and then the ‘Unload AD2’ button on UI1. It doesn’t work every time on my box, but in about half of all attempts I get the CannotUnloadAppDomainException. To increase your chances:) you may have to hit the ‘Start Thread’ several times to create more threads, especially if you run this app outside VS.
Regards, SS
.. and here is the code:
using System; using System.Threading; using System.Windows.Forms; using System.Drawing; using System.Reflection;
namespace AppDomains { public class Test : MarshalByRefObject { static void Main() { Test t1 = new Test(); t1.StartUI1(); }
public void StartUI1() { Thread t = new Thread(ShowUI1); t.SetApartmentState(ApartmentState.STA); t.Start(); }
private void ShowUI1() { AppDomain ad2 = AppDomain.CreateDomain("AD2"); // You might need to change the type name here... Test t2 = (Test)ad2.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "AppDomains.Test"); t2.StartUI2();
Form form = new Form { Size = new Size(200, 200), Text = "Form UI1" };
Button b = new Button { Text = "Unload AD2", Location = new Point(5, 50) };
b.Click += (s, e) => { AppDomain.Unload(ad2); }; form.Controls.Add(b); Application.Run(form); }
public void StartUI2() { Thread t = new Thread(ShowUI2); t.SetApartmentState(ApartmentState.STA); t.Start(); }
private void ShowUI2() { Form form = new Form { Size = new Size(200, 200), Text = "Form UI2" };
Button b = new Button { Text = "Start Thread", Location = new Point(5, 50) };
b.Click += (s, e) => { Thread t = new Thread(T); t.Start(); };
form.Controls.Add(b); Application.Run(form); }
private void T() { while (true) { }; } } }
Jeffrey Tan[MSFT] - 16 Apr 2008 10:42 GMT Hi Sunny,
Thanks for your feedback.
I was able to reproduce this problem intermittently about 1 out of 5. However, whenever I run it under windbg, the problem did not appear. I will spend more time on this issue and get back to you ASAP. Thanks.
Best regards, Jeffrey Tan Microsoft Online Community Support ========================================= Delighting our customers is our #1 priority. We welcome your comments and suggestions about how we can improve the support we provide to you. Please feel free to let my manager know what you think of the level of service provided. You can send feedback directly to my manager at: msdnmg@microsoft.com.
This posting is provided "AS IS" with no warranties, and confers no rights.
Willy Denoyette [MVP] - 16 Apr 2008 11:17 GMT > Hi Sunny, > [quoted text clipped - 17 lines] > This posting is provided "AS IS" with no warranties, and confers no > rights. I'm not able to reproduce (running V2 of the FW), which is quite normal when you run this code on a quad core as I do. However if you run this code on a single core system, chances are higher to hit the problem, which is the documented behavior (see AppDomain.Unload in the docs). AppDomain.Unload is constrained by a time-out period, failing to unload within that period of time will throw CannotUnloadAppDomainException. Now, in the OP's case, there is a thread that continuously consumes all of it's thread quanta, if you have only one CPU in the system, chances are high that the "dedicated thread" responsible for unloading the AD, will fail to inject a Thread.Abort in a timely fashion. On a multi-core, chances are much lower to fail, while on a normally loaded quad core, chances are extremely low to fail.
Willy.
Sunny S - 16 Apr 2008 12:34 GMT Hi Willi,
Yes, I am running a single core PC and your explanation fits very well into a simplistic mental picture of what was happening that I developed for myself. Thank you very muich again.
Regards,
SS
> I'm not able to reproduce (running V2 of the FW), which is quite normal when > you run this code on a quad core as I do. However if you run this code on a [quoted text clipped - 10 lines] > > Willy. Willy Denoyette [MVP] - 16 Apr 2008 15:15 GMT Hi Sunny,
Note that even on a multi-core, you aren't guaranteed to be able to unload an AD, everything depends on the system's configuration and the CPU load, and that's also what the doc tells us. Even on a quad-core, I'm was able to reproduce the issue, this by starting four threads like yours. However, running with normal workload, you won't see this happen, and if you do, you can always try to handle the exception and retry the AD unload.
Willy.
> Hi Willi, > [quoted text clipped - 28 lines] >> >> Willy. Jeffrey Tan[MSFT] - 17 Apr 2008 09:33 GMT Hi Willy ,
This sounds like a promising explanation. Thank you for the sharing.
However, from principle, I am still a bit hard to understand it. The started worker threads should run under the same priority as the GUI threads. Even on the single-core CPU, OS kernel thread schedular will give each thread the same time slice as long as these threads are in the same priority. It is hard for me to understand why the CPU gives not enough time to the GUI thread to call Thread.Abort() method.
Furthermore, if I remember correctly, the CPU will boost the UI thread's priority making it more user friendly. So the GUI thread should have higher priority(at least a small period) to execute the Thread.Abort() method.
Additionally, since the Thread.Abort() will cause the user-mode to kernel-mode transition with stack-unwind which are both very consuming, I suspect if this is another important factor for AppDomain.Unload() expires.
Anyway, I agree that real-world seldom causes this problem. Catching this CannotUnloadAppDomainException and call AppDomain.Unload() again should resolve this problem.
Best regards, Jeffrey Tan Microsoft Online Community Support ========================================= Delighting our customers is our #1 priority. We welcome your comments and suggestions about how we can improve the support we provide to you. Please feel free to let my manager know what you think of the level of service provided. You can send feedback directly to my manager at: msdnmg@microsoft.com.
This posting is provided "AS IS" with no warranties, and confers no rights.
Willy Denoyette [MVP] - 17 Apr 2008 12:03 GMT Hi Jeffrey,
See inline.
Willy.
> Hi Willy , > > This sounds like a promising explanation. Thank you for the sharing. Well, actually I'm not so sure it's a valid explanation, especially because I can't seem to repro the issue, even not when running four threads on a quad-core running Vista SP1.
> However, from principle, I am still a bit hard to understand it. The > started worker threads should run under the same priority as the GUI [quoted text clipped - 3 lines] > time > to the GUI thread to call Thread.Abort() method. Apparently there is a dedicated thread that serves to unload AD's in V2 (see the "Remarks" section in the AppDomain.Unload description), that means that AD Unloads are not executed on the UI thread. I assume they would use the Finalizer thread for this, but I can't confirm this for sure.
> Furthermore, if I remember correctly, the CPU will boost the UI thread's > priority making it more user friendly. So the GUI thread should have > higher > priority(at least a small period) to execute the Thread.Abort() method. True, but irrelevant when the Thread.Abort is issued by another (the dedicated) thread that runs with "normal" priority level., if it's done by the finalizer thread then there shouldn't be an issue as this one run at real-time priority level.
> Additionally, since the Thread.Abort() will cause the user-mode to > kernel-mode transition with stack-unwind which are both very consuming, I [quoted text clipped - 4 lines] > CannotUnloadAppDomainException and call AppDomain.Unload() again should > resolve this problem.
> Best regards, > Jeffrey Tan [quoted text clipped - 8 lines] > This posting is provided "AS IS" with no warranties, and confers no > rights. Jeffrey Tan[MSFT] - 23 Apr 2008 07:21 GMT Hi Willy,
Thank you for pointing out the document.
Yes, it seems that these issues are documented in MSDN clearly. CannotUnloadAppDomainException is expected to throw in some scenarios. Catching CannotUnloadAppDomainException and calling AppDomain.Unload again should be the solution. Also, I think we'd better give a guard to this. For example, we should check if this will result in an infinite catch...re-calling loop. If so, I think we have to kill the threads in the target AppDomain to recover from this situation. Yes, this may not be an elegant solution, but we have to defense this situation.
Thanks.
Best regards, Jeffrey Tan Microsoft Online Community Support ========================================= Delighting our customers is our #1 priority. We welcome your comments and suggestions about how we can improve the support we provide to you. Please feel free to let my manager know what you think of the level of service provided. You can send feedback directly to my manager at: msdnmg@microsoft.com.
This posting is provided "AS IS" with no warranties, and confers no rights.
Ben Voigt [C++ MVP] - 23 Apr 2008 15:43 GMT > Hi Willy, > [quoted text clipped - 9 lines] > this may not be an elegant solution, but we have to defense this > situation. Is there any supported way to enumerate all Threads? All Threads running code from a particular AppDomain? All Threads started by a particular AppDomain? Nevermind the problems with threads ignoring abort because they are in an I/O call or other native code.
> Thanks. > [quoted text clipped - 10 lines] > This posting is provided "AS IS" with no warranties, and confers no > rights. Sunny S - 23 Apr 2008 20:52 GMT Hi Ben,
1. The Threads property of a Process instance should give you the set of threads that are running in a given process or you can just use Process.GetCurrentProcess.Threads.
2. Then for each thread in the ProcessThreadCollection returned by the Threads property in (1) you can use the GetDomainID property to find which AppDomains currently contain running threads and to map those threads to individual AppDomains.
3. I am not sure if my speculations about your 3rd question are correct, but I think that given the fact that AppDomains could be unloaded and then recreated, even reused, I suspect, which is certainly true for threads provided by the ThreadPool, associating an AppDomain that started a particular thread with this thread or, the other way round, associating a thread with an AppDomain that started this thread might lead to confusion and/or could be costly. I doubt that the runtime supports this. The only valid information would be provided by an instant snapshot of which threads are running in which AppDomains (see 2 above).
Regards,
Sunny
> > Hi Willy, > > [quoted text clipped - 29 lines] > > This posting is provided "AS IS" with no warranties, and confers no > > rights. Ben Voigt [C++ MVP] - 24 Apr 2008 16:42 GMT > Hi Ben, > [quoted text clipped - 17 lines] > provided by an instant snapshot of which threads are running in which > AppDomains (see 2 above). I was just trying to highlight the possibility that just because a thread is currently running code in one AppDomain, doesn't mean it doesn't have other AppDomains on the call stack and aborting the thread will interfere with those other AppDomains. Or are cross-AppDomain calls always marshalled to a thread dedicated to that AppDomain?
Consider for example the case where the plugin subscribed to an event in the main program. When the main program fires the event handlers, the plugin handler runs and hangs. How do you unload the AppDomain without throwing a ThreadAbort exception up through the main program method which raised the event?
> Regards, > [quoted text clipped - 33 lines] >>> This posting is provided "AS IS" with no warranties, and confers no >>> rights. Willy Denoyette [MVP] - 24 Apr 2008 16:49 GMT >> Hi Ben, >> [quoted text clipped - 27 lines] > the main program. When the main program fires the event handlers, the > plugin handler runs and hangs. What exactly do you mean with hangs? Does it mean that the thread has stack in unmanaged land or what?
Willy.
Sunny S - 24 Apr 2008 20:05 GMT Hi Ben,
There is no such thing as a thread being ‘dedicated to an AppDomain’. A cross-AD call stays on a thread that originated the call; marshalling happens between ADs, not between threads. Let’s say your thread TH1 is running in AD1 and you call a method on an object derived from MarshallByReference and sitting in AD2. Then your call goes through the object’s proxy in AD1, crosses domain boundary, and hits the real object in AD2 while still remaining on TH1!
As far as the aborted thread’s call stack is concerned and possible interference with ADs that might be on the stack, that’s exactly what sometimes happens. After TH1 leaves AD1 and enters AD2, AD1 could be unloaded. Unloading AD1 would result in ThreadAbortException being thrown on TH1 even if it is in AD2! See for more details Steve Pratschner’s book ‘Customizing the Microsoft .NET Framework CLR’
Regarding your example when you have to kill a thread hanging in a plug-in that sits in a different AD by unloading that AD, I am not sure, but somehow unwinding ThreadAbortException doesn’t cross AD boundary even if other ADs are on the thread’s call stack, so the original caller in a different AD isn't hurt:)
Regards,
Sunny
> I was just trying to highlight the possibility that just because a thread is > currently running code in one AppDomain, doesn't mean it doesn't have other [quoted text clipped - 9 lines] > > > Regards, Jeffrey Tan[MSFT] - 24 Apr 2008 03:49 GMT Hi Ben ,
I think #1, #2 provided Sunny is the .Net FCL's build-in support for the thread/process enumeration. However, there is no much build-in support to terminate/kill the freeze threads. Maybe we have to p/invoke TermianteThread ourselves.(For example, .Net Process.Kill calls TerminateProcess internally). We definite should use this as a last resort.
Thanks.
Best regards, Jeffrey Tan Microsoft Online Community Support ========================================= Delighting our customers is our #1 priority. We welcome your comments and suggestions about how we can improve the support we provide to you. Please feel free to let my manager know what you think of the level of service provided. You can send feedback directly to my manager at: msdnmg@microsoft.com.
This posting is provided "AS IS" with no warranties, and confers no rights.
Willy Denoyette [MVP] - 04 Apr 2008 16:47 GMT > Hi, > [quoted text clipped - 6 lines] > > Sunny S. Application.Exit() is a Forms API, WPF does not run a message loop so A.E makes no sense in WPF.
Willy.
Barry Kelly - 05 Apr 2008 02:50 GMT > Application.Exit() is a Forms API, That is true.
> so A.E makes no sense in WPF. That may be strictly true, but try this on for size:
---8<--- using System; using System.Windows; using System.Threading; using System.Runtime.InteropServices; using System.Windows.Controls;
public class App { [DllImport("user32.dll")] static extern void PostQuitMessage(int nExitCode); static Button MakeQuitButton() { Button result = new Button(); result.Content = "Quit"; result.Click += (s,e) => PostQuitMessage(0); return result; } [STAThread] static void Main() { new Application().Run(new Window() { Content = MakeQuitButton() }); } } --->8---
> WPF does not run a message loop This is empirically false:
---8<--- ntkrnlpa.exe!KiSwapContext+0x2f ntkrnlpa.exe!KiSwapThread+0x8a ntkrnlpa.exe!KeWaitForSingleObject+0x1c2 ntkrnlpa.exe!KiSuspendThread+0x18 ntkrnlpa.exe!KiDeliverApc+0x124 ntkrnlpa.exe!KiSwapThread+0xa8 ntkrnlpa.exe!KeWaitForSingleObject+0x1c2 win32k.sys!xxxSleepThread+0x192 win32k.sys!xxxRealInternalGetMessage+0x418 win32k.sys!NtUserGetMessage+0x27 ntkrnlpa.exe!KiFastCallEntry+0xfc ntdll.dll!KiFastSystemCallRet USER32.dll!NtUserGetMessage+0xc WindowsBase.ni.dll+0x5e623 WindowsBase.ni.dll!ResetValue+0xd WindowsBase.ni.dll!get_SourceProperty+0x1 WindowsBase.ni.dll!.ctor+0xe WindowsBase.ni.dll!ConvertToString+0x21 WindowsBase.ni.dll!ConvertToString+0x2 PresentationFramework.ni.dll!AlignContent+0x7b mscorwks.dll!CallDescrWorkerWithHandler+0xa3 mscorwks.dll!MethodDesc::CallDescr+0x19c mscorwks.dll!MethodDesc::CallTargetWorker+0x1f mscorwks.dll!MethodDescCallSite::Call_RetArgSlot+0x18 mscorwks.dll!ClassLoader::RunMain+0x263 mscorwks.dll!Assembly::ExecuteMainMethod+0xa6 mscorwks.dll!SystemDomain::ExecuteMainMethod+0x43f mscorwks.dll!ExecuteEXE+0x59 mscorwks.dll!_CorExeMain+0x15c mscoree.dll!_CorExeMain+0x2c KERNEL32.dll!BaseProcessStart+0x23 --->8---
NtUserGetMessage is there, clear as day.
-- Barry
 Signature http://barrkel.blogspot.com/
Willy Denoyette [MVP] - 07 Apr 2008 14:32 GMT >> Application.Exit() is a Forms API, > [quoted text clipped - 34 lines] > } > --->8--- Well here you are calling PostQuitMessage which doesn't really do what it's supposed to do, also System.Windows.Forms.Application.Exit doesn't do what it has to do here (in a WPF based app.). Take a look at the messages using Spyxx.exe, you'll see that none of the API's are sending WM_QUIT message to your application. A.E does not terminate the UI thread whereas PostQuitMessage simply terminates the UI thread and as such the application. If you are looking for the equivalent of A.E in WPF, then take a look at Application.Shutdown, PostQuitMessage shouldn't be used at all.
>> WPF does not run a message loop > > This is empirically false: Well, above is quite confusing, I should have been more explicit. Every Windows Application, no matter what the UI framework used , will have at least one top level window, this is also true for WPF based application. A minimal WPF application has at least one single Window (HWND) that is used as a *host* or wrap WPF. This top level Window, with it's associated message loop and WndProc, receives/handles all Windows messages for the application, WPF itself is not HWND based, Windows messages are translated (when appropriate) into CLR events that get routed to the the WPF Visuals. This top level HWND isn't exposed and accessible like it is in other frameworks like Windows.Forms, you can't override WndProc for instance,you shouldn't even assume such Window is present. Note also that the WPF core registers Windows messages that aren't know to Windows.Forms and vice-versa, that's why there is an WPF/Forms interop layer in the form of the HwndHost control.
Willy.
Barry Kelly - 07 Apr 2008 19:08 GMT > >> Application.Exit() is a Forms API, > > > > That is true.
> Well here you are calling PostQuitMessage which doesn't really do what it's > supposed to do PostQuitMessage posts WM_QUIT to the current thread's message queue. This is no different in a WPF application than in any other Windows application. WM_QUIT causes GetMessage to return 0.
WindowsBase.dll, System.Windows.Threading.Dispatcher::PushFrameImpl() contains the message loop which calls GetMessage. It exits the loop when GetMessage returns 0.
It's a pretty simple stack from there on out to exit the application, provided you don't have extra code after the Application.Run() call.
---8<---
|> 0:000> !clrstack |> OS Thread Id: 0x1c68 (0) [quoted text clipped - 7 lines] |> 0012f45c 5533db15 System.Windows.Application.Run(System.Windows.Window) |> 0012f46c 00c5010d App.Main() --->8---
>, also System.Windows.Forms.Application.Exit doesn't do what > it has to do here (in a WPF based app.). I never said it did.
> Take a look at the messages using > Spyxx.exe, you'll see that none of the API's are sending WM_QUIT message to > your application. Did you look at the window messages or the thread messages? WM_QUIT isn't normally sent to the windows, just the thread message queue.
SpyXX on my system crashes when it tries to hook WPF thread messages, but WinDbg shows the thread message queue calls just fine (as indicated above).
> PostQuitMessage simply terminates the UI thread and as such the application. No it doesn't.
It posts a message to the thread message queue and then returns to its caller immediately.
This can be verified in the debugger, or simply by adding code to the appropriate places in a sample application:
---8<--- using System; using System.Windows; using System.Threading; using System.Runtime.InteropServices; using System.Windows.Controls;
public class App { [DllImport("user32.dll")] static extern void PostQuitMessage(int nExitCode); static Button MakeQuitCButton() { Button result = new Button(); result.Content = "Quit"; result.Click += (s,e) => { Console.WriteLine("Before Quit message"); PostQuitMessage(0); Console.WriteLine("After Quit message"); }; return result; } [STAThread] static void Main() { Console.WriteLine("Before Application.Run"); new Application().Run(new Window() { Content = MakeQuitCButton() }); Console.WriteLine("After Application.Run"); } } --->8---
This shows the following on my machine, after I press the Quit button:
---8<--- Before Application.Run Before Quit message After Quit message After Application.Run --->8---
> If you are looking for the equivalent of A.E in WPF, then take a look at > Application.Shutdown, PostQuitMessage shouldn't be used at all. I agree with you, one should use Application.Shutdown. You end up missing out a whole load of app teardown logic if you use PostQuitMessage.
> >> WPF does not run a message loop > > [quoted text clipped - 5 lines] > A minimal WPF application has at least one single Window (HWND) that is used > as a *host* or wrap WPF. I never asserted that WPF was based on windows all the way down to each control - that would limit its functionality and performance quite severely, and you wouldn't get all the scene graph possibilities with transforms, composing in accelerated hardware etc, without undesirable clipping. Not even non-WPF applications use windows all the way down, including IE, Firefox, etc. See also e.g. TGraphicControl in Delphi (mouse-only input), etc. WPF is in no way new or original in this respect.
You did, however, appear to assert that WPF applications don't have a message loop, which is nonsense.
> This top level Window, with it's associated message loop and WndProc, > receives/handles all Windows messages for the application, There will be at least as many HWNDs as there are separate top-level windows for that application on the desktop, each one with an appropriate WNDPROC.
-- Barry
 Signature http://barrkel.blogspot.com/
Willy Denoyette [MVP] - 07 Apr 2008 21:28 GMT >> >> Application.Exit() is a Forms API, >> > [quoted text clipped - 7 lines] > This is no different in a WPF application than in any other Windows > application. WM_QUIT causes GetMessage to return 0. Well, It doesn't post a WM_QUIT message to the UI's thread queue when run on Vista (both RTM and SP1), of course it does post WM_QUIT when called in a Windows.Forms based application (or any non managed windows app.).
> WindowsBase.dll, System.Windows.Threading.Dispatcher::PushFrameImpl() > contains the message loop which calls GetMessage. It exits the loop when [quoted text clipped - 36 lines] > Did you look at the window messages or the thread messages? WM_QUIT > isn't normally sent to the windows, just the thread message queue. I know that.
> SpyXX on my system crashes when it tries to hook WPF thread messages, > but WinDbg shows the thread message queue calls just fine (as indicated > above). SpyXX has some troubles to enumerate "managed" processes when run on 32bit, and may fail to inject the hook into a managed process on Vista32. The only combination I found working reliably is: spyxx.exe 64-bit on Vista64 running with debug privileges. Beware that once SpyXX has failed to run, it will probably fail until after reboot.
>> PostQuitMessage simply terminates the UI thread and as such the >> application. > > No it doesn't. It terminates the application without posting a WM_QUIT message on Vista, instead it sends a private registered message (amongs a bunch of others) to the dispatcher which finally terminates the session and returns from Run.
> It posts a message to the thread message queue and then returns to its > caller immediately. It doesn't post a WM_QUIT message on Vista. This is different from Windows Forms which posts a WM_QUIT message (amongs other messages to the UI thread's queue).
> This can be verified in the debugger, or simply by adding code to the > appropriate places in a sample application: [quoted text clipped - 45 lines] > After Application.Run > --->8--- Oh, but I never said that the Click handler wasn't called! I only said that there was no WM_QUIT message posted, sure I should have mentioned "when run on VISTA!" Note also that the CLR stack looks somewhat different when run on Vista-64, the unmanaged stack is also quite different from what you get on Vista32 and XP.
OS Thread Id: 0x1af4 (0) Child-SP RetAddr Call Site 000000000064e520 000006422da9f1c1 DomainBoundILStubClass.IL_STUB(System.Windows.Interop.MSG ByRef, Int32, Int32, Int32, Boolean ByRef) 000000000064e6e0 000006422da9efef System.Windows.Threading.Dispatcher.GetMessage(System.Windows.Interop.MSG ByRef, IntPtr, Int32, Int32) 000000000064e790 000006422da9eefe System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame) 000000000064e860 000006422a5cc061 System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame) 000000000064e8c0 000006422a5cbd4d System.Windows.Application.RunInternal(System.Windows.Window) 000000000064e930 0000064280150187 System.Windows.Application.Run(System.Windows.Window) 000000000064e980 000006427f67ba32 App.Main()
Unmanaged stack..
0:000> kb RetAddr : Args to Child
: Call Site 00000000`772dd0ce : 0000988e`36afa07e 00000000`006c8bc0 00000000`00000000 00000000`00000000 : USER32!ZwUserGetMessage+0xa 000007fe`fde8589e : 00000000`006c8bc0 00000000`00000001 00000000`00000000 00000642`7f88d142 : USER32!GetMessageW+0x34 00000642`7f679b67 : 00000000`0051ebc0 00000000`006c8bc0 0000988e`37354ec0 00000642`7f3d6ac0 : MSCTF!CThreadInputMgr::GetMessageW+0x42 *** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v2.0.50727_64\WindowsBase\3fd6671f329e84b22e22624974ade4a3\WindowsBase.ni.dll 00000642`2da9fc23 : 00000000`1b473cc0 00000000`006c8bc0 00000000`006c8bc0 00000642`2dd2a780 : mscorwks!DoCLRToCOMCall+0x177 00000642`2da9f1c1 : 00000000`0051f190 00000000`0051e9c1 00000000`0051eab8 00000000`0051ebc0 : WindowsBase_ni!GetLocalizedFailureCodeMessage+0xad [d:\SP1\windows\wcp\Base\MS\Internal\Security\RightsManagement\Errors.cs @ 93] 00000642`2da9efef : 00000000`02db7318 00000000`0051ebc0 00000000`00000000 00000000`00000000 : WindowsBase_ni!GetContentIdFromPublishLicense+0x3 [d:\SP1\windows\wcp\Base\MS\Internal\Security\RightsManagement\ClientSession.cs @ 1762] 00000642`2da9eefe : 00000000`02da5a50 00000000`0051eca0 00000000`02d86ee8 00000000`02db9058 : WindowsBase_ni!GetContentIdFromLicense+0x76 [d:\SP1\windows\wcp\Base\MS\Internal\Security\RightsManagement\ClientSession.cs @ 1489] *** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v2.0.50727_64\PresentationFramewo#\0faafa8d4d1c47956dbdb97aaebc9715\PresentationFramework.ni.dll 00000642`2a5cc061 : 00000000`02e660c8 00000000`02db7318 00000000`02db7214 feeefeee`feeefeee : WindowsBase_ni!ExtractApplicationSpecificDataFromLicense+0x18 [d:\SP1\windows\wcp\Base\MS\Internal\Security\RightsManagement\ClientSession.cs @ 1407] 00000642`2a5cbd4d : 00000000`02d86ee8 00000000`02db9058 00000000`0000000a 00000000`00000014 : PresentationFramework_ni!get_ChildTextView+0x22 [d:\SP1\windows\wcp\Framework\System\Windows\Documents\documentsequencetextview.cs @ 610]
>> If you are looking for the equivalent of A.E in WPF, then take a look at >> Application.Shutdown, PostQuitMessage shouldn't be used at all. [quoted text clipped - 27 lines] > You did, however, appear to assert that WPF applications don't have a > message loop, which is nonsense. I said *WPF* did not have a message loop, I didn't say *WPF applications*, but as sa id before, I should have been more explicit. WPF doesn't actually knows anything about Windows messages and queues, it's the host (the top level frame (a HwndWrapper)) that provides the message queue and the wndproc not the framework (WPF) .
>> This top level Window, with it's associated message loop and WndProc, >> receives/handles all Windows messages for the application, > > There will be at least as many HWNDs as there are separate top-level > windows for that application on the desktop, each one with an > appropriate WNDPROC. Yep, there are"visible" HWND's for the application frame , the menu's and some other stuff (all top-level windows) plus a bunch of hidden Windows (the # of them depending on the OS version), that's why I said at least one HWND per application.
Willy.
Ben Voigt [C++ MVP] - 08 Apr 2008 15:09 GMT >>>>> Application.Exit() is a Forms API, >>>> [quoted text clipped - 12 lines] > called in a Windows.Forms based application (or any non managed > windows app.). Barry has this one exactly right. PostQuitMessage is a Win32 function, it couldn't care less whether your app is WPF or WinForms or MFC or whatever. It posts WM_QUIT and returns.
I don't know how you can get any clearer than this:
http://msdn2.microsoft.com/en-us/library/ms644945(VS.85).aspx "The PostQuitMessage function posts a WM_QUIT message to the thread's message queue and returns immediately; the function simply indicates to the system that the thread is requesting to quit at some time in the future."
Willy Denoyette [MVP] - 08 Apr 2008 18:15 GMT >>>>>> Application.Exit() is a Forms API, >>>>> [quoted text clipped - 24 lines] > the system that the thread is requesting to quit at some time in the > future." The docs are clear, but I'm afraid they are missing some additional remarks when run on Vista (don't know what happens on XP SP2 and SP3). Please don't point to the docs to prove I'm wrong, use some debugging tools and correct me if you notice the *documented* behavior. Running SpyXX [1] doesn't reveal any WM_QUIT message for a WPF enabled application (say the sample posted by Barry), it does (as expected) when called from any other type of application (managed Forms, and native MFC and Win32). If you can't get Spyxx (from VS2008) to work for you, or if you don't fully trust what SPxx reveals(like I do), you can always run the app in the debugger (say Windbg), set a breakpoint on GetMessageW and inspect the MSG struct returned (there are quite a lot of them) when you call the API, you won't see any WM_QUIT message (at least I don't).
Willy.
On Vista32 SP1 and Vista64 SP1 and WS2008, all running the same v3.5 Framework bits.
Ben Voigt [C++ MVP] - 08 Apr 2008 23:48 GMT >>>>>>> Application.Exit() is a Forms API, >>>>>> [quoted text clipped - 39 lines] > you call the API, you won't see any WM_QUIT message (at least I > don't). You really should break on PostMessage and PostThreadMessage (A and W variants), because it's entirely possible that PostQuitMessage did exactly what the documentation described, but the .NET method (Application.Shutdown I guess) triggered ending the message loop so GetMessage was never called.
For example: put this code in any of the application types where you do see WM_QUIT:
PostQuitMessage(); ExitProcess();
Clearly the behavior of PostQuitMessage hasn't changed, yet you'll never see WM_QUIT returned by GetMessage.
> Willy. > > On Vista32 SP1 and Vista64 SP1 and WS2008, all running the same v3.5 > Framework bits. Willy Denoyette [MVP] - 09 Apr 2008 13:30 GMT >>>>>>>> Application.Exit() is a Forms API, >>>>>>> [quoted text clipped - 54 lines] > > see > WM_QUIT returned by GetMessage. Hmmm, how do you expect see a WM_QUIT when you terminate the process directly after PostQuitMessage ?
Also, I did break on PostQuitMessage (there are no A or W variants) don't worry, your assumption that PQM has not changed may be valid for the "user" portion, which is a small thunk [1] that calls into the kernel (syscall), that's the place where messages are constructed, and that's where things got changed dramatically in Vista, that's the portion that knows about "Avalon" and the DWM. That's the portion that needs to coordinate with the DWM composition, that's the part that handles PQM when called from a WPF application running on Vista with DWM (glass) enabled.
[1] 0:000> uf user32!postquitmessage USER32!PostQuitMessage: 00000000`76e90918 4863c9 movsxd rcx,ecx 00000000`76e9091b ba34000000 mov edx,34h 00000000`76e90920 e94bcc0000 jmp USER32!ZwUserCallOneParam (00000000`76e9d570)
USER32!ZwUserCallOneParam: 00000000`76e9d570 4c8bd1 mov r10,rcx 00000000`76e9d573 b802100000 mov eax,1002h 00000000`76e9d578 0f05 syscall 00000000`76e9d57a c3 ret
Once again, the message loop is not terminated as a result of a WM_QUIT message put in the message queue, none of the GetMessage calls return 0 once PQM has been called, that means none of them have "picked-up" a WM_QUIT. It's the DWM that stops the message loop, which doesn't mean that PQM is the right API to call from WPF (it isn't). You should start to convince yourself, I can't post more the debugger output here. Start a kernel debugger session and look what PQM does in the kernel, do the same thing when calling Application.Shutdow, look how thing got coordinated with the DWM (using ALPC's ) over you will understand what I mean here. Don't be surprised when you got blocked by DRM in kernel debugger, don't be surprised to see some COM interaction between the processes and don't be surprised to find a some undocumented Windows messages in the application queue.
Willy.
Sunny S - 10 Apr 2008 09:55 GMT Keep hitting wrong buttons, sorry...
Gentlemen, thank you very much for an interesting discussion, from which I learned a lot, but I feel I had to be more specific.
1. Let’s say I’ve started a SimpleWinForm application. I presume that means starting a message loop.
2. From within that application I create an AppDomain in which I create a new thread on which I call Application.Run(new SomeOtherWinForm()). See, for example, Jon Skeet’s code above except that the code should be running on a dedicated thread in a separate AppDomain.
Microsoft documentation says that Application.Exit() method ‘Informs ALL message pumps that they must terminate, and then closes all application windows after the messages have been processed’.
From the above I would conclude that upon calling Application.Exit() both SomeOtherWinForm and SimpleWinForm should close and the whole thing should terminate. Experiments show that this is not the case – only SomeOtherWinForm closes.
Again, Microsoft documentation for System.Windows.Application (not System.Windows.Forms.Application!) says that there is only one instance of Application object per AppDomain. It looks as if the same relationship is true for System.Windows.Forms.Application objects and AppDomains, because this would explain the above behaviour, but I wasn’t sure.
SS
> Hi, > [quoted text clipped - 6 lines] > > Sunny S. Willy Denoyette [MVP] - 10 Apr 2008 10:56 GMT > Keep hitting wrong buttons, sorry... > [quoted text clipped - 29 lines] > > SS When the documentation says "all running message loops on all threads and closes all windows of the application." it really should say in the callers AppDomain (AD) and it's owning AD's. That means, that if you call Application.Exit in a child AD , only the child AD windows will be affected. If, however you cal A.E from the parent AD , then also the child AD windows will be affected. Note also that Windows.Forms was not designed to be run from within multiple domains in a single process.
Willy.
Sunny S - 10 Apr 2008 11:35 GMT Willy,
Thanks very much for a quick reply. Can you see any immediate problems I am going to run into if I use Windows.Forms in multiple AppDomains?
SS
> > Keep hitting wrong buttons, sorry... > > [quoted text clipped - 40 lines] > > Willy. Sunny S - 10 Apr 2008 19:06 GMT Hi Willy,
Again, thanks for the reply. You might be interested in reading (and, perhaps, commenting on:)) my reply to Jon Skeet above. I've done some experiments to clarify dependecies between message pumps running in different AppDomains with regards to Application.Exit()
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|