Re: Relationship between Application.Exit() and AppDomain



"Barry Kelly" <barry.j.kelly@xxxxxxxxx> wrote in message news:4djkv39krr67nr7f3fjttfi86581in3gpm@xxxxxxxxxx
Willy Denoyette [MVP] wrote:

"Barry Kelly" <barry.j.kelly@xxxxxxxxx> wrote:

> Willy Denoyette [MVP] wrote:
>
>> 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.


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
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)
|> ESP EIP
|> 0012f358 56d8e623 MS.Win32.UnsafeNativeMethods.GetMessageW(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)
|> 0012f36c 56d6197e System.Windows.Threading.Dispatcher.GetMessage(System.Windows.Interop.MSG ByRef, IntPtr, Int32, Int32)
|> 0012f3b8 56d617e3 System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)
|> 0012f408 56d616c7 System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)
|> 0012f424 56d6162d System.Windows.Threading.Dispatcher.Run()
|> 0012f430 5533dce0 System.Windows.Application.RunInternal(System.Windows.Window)
|> 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.


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:

---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---

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.

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
>
> 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.

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.

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.




.