ShowDialog() Sucks: Use ApplicationContext and Run Instead

Update Jan 7: Added KeyboardInterop so that TextBoxes will work.

One of the lessons learned when making Popups and NotifyIcons in PowerShell and WPF is that the WPF window must be run the the proper context.

I was tipped off to this by Denniver Reining’s PowerShell / NotifyIcon article and Johnny J’s “Doing a (C#) NotifyIcon program the right way“.

While neither of the articles were using WPF, extensive testing showed that using ApplicationContexts and Application.Run instead of ShowDialog() made WPF (and WinForms) work far better. The NotifyIcon issue took a number of days to resolve as I battled with unresponsiveness when clicking on the ContextMenu to Exit. Then it would take about 5 seconds to disappear.

For about a week, this was the story of my life:


And it seemed especially true after hiding the Powershell taskbar application. So I scoured the Internet and found that a lot of other people had the same issue. Just search Google for “NotifyIcon doesn’t disappear.

In addition, the mouse sometimes showed as busy when hoovering over the popup window itself and sometimes the right clicking worked only once.


System.Windows.Forms to the rescue (!?)

So none of the solutions I found worked, but I remembered that Denniver’s NotifyIcon script was responsive, so I went back and noticed his app ended with this important line


This information, along with Johnny J’s article about NotifyIcons helped me figure out that the following would probably work, even for WPF. Now these two lines, along with Hide-PowerShell are always included in my finalized PowerShell-based WPF scripts.

$appContext = New-Object System.Windows.Forms.ApplicationContext

According to Microsoft, “Application.Run begins running a standard application message loop on the current thread, with an ApplicationContext, which specifies the contextual information about an application thread.”

I’ll be honest, I’m probably not the best person to explain ApplicationContexts in depth. If you’d like to learn more, check out this article titled Use the ApplicationContext Class to Fully Encapsulate Splash Screen Functionality where the author goes into detail about the ApplicationContext class.

The code below (which is also the code from my previous post) shows a fully functioning WPF GUI App that runs in an application with an ApplicationContext. It also has a few other cool techniques you may enjoy.


Note that this code should’t be copy/pasted into the console because the PowerShell window will disappear before it can complete the paste. Be sure to download the script or paste the code below into a .ps1 file and execute. It does appear to work well in the ISE, though!

# Add required assemblies
Add-Type -AssemblyName PresentationFramework, System.Drawing, System.Windows.Forms, WindowsFormsIntegration
# Setup the XAML
[xml]$script:xaml = '
# Create the form and set variables
$script:window = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $xaml))
$xaml.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name ($_.Name) -Value $window.FindName($_.Name) -Scope Script }
# here's the base64 string of the image
# Create a streaming image by streaming the base64 string to a bitmap streamsource
$bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
$bitmap.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($base64)
# This is the pic in the middle
$image.source = $bitmap
# This is the icon in the upper left hand corner of the app
$window.Icon = $bitmap
# This is the toolbar icon and description
$window.TaskbarItemInfo.Overlay = $bitmap
$window.TaskbarItemInfo.Description = $window.Title
# Add Exit (Thanks, Ryan!)
$window.Add_Closing({[System.Windows.Forms.Application]::Exit(); Stop-Process $pid})
# Make PowerShell Disappear 
$windowcode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);' 
$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru 
$null = $asyncwindow::ShowWindowAsync((Get-Process -PID $pid).MainWindowHandle, 0) 

# Allow input to window for TextBoxes, etc

# Running this without $appContext and ::Run would actually cause a really poor response.

# This makes it pop up
# Create an application context for it to all run within. 
# This helps with responsiveness and threading.
$appContext = New-Object System.Windows.Forms.ApplicationContext 

Even though I had changed my own usage of WPF forms, it didn’t occur to me to blog about it until Doug Finke mentioned on Twitter that his WPF apps are sometimes unstable and crash. When I saw the ShowDialog() in his code, I knew exactly what the problem was, and knew I had to get the word out.

As of today, one of my servers has been running its WPF/PowerShell based-monitor for almost a month!


In conclusion, you should probably never use ShowDialog() to run your primary window in PowerShell unless you’re testing it (and I should probably update my old blog posts to clarify that).

Also, I don’t totally know what I’m doing, so take this with a grain of salt ;) It’s just worked better for me than ShowDialog(). I will probably revisit this one day with a PowerShell implementation of InitializeComponent().

Chrissy is a Cloud and Datacenter Management & Data Platform MVP who has worked in IT for over 20 years. She is the creator of the popular SQL PowerShell module dbatools, holds a master's degree in Systems Engineering and is coauthor of Learn dbatools in a Month of Lunches. Chrissy is certified in SQL Server, Linux, SharePoint and network security. You can follow her on Twitter at @cl.

Posted in PowerShell, WPF
15 comments on “ShowDialog() Sucks: Use ApplicationContext and Run Instead
  1. One issue with your code is the PowerShell session doesn’t close when the window is closed. I ran your script as instructed, closed the dialog, and the PowerShell session is still running. After taking your hide window code out, I re-ran. After closing the window the script was still running.

    I’m not sure if this is just me or what, but you may want to do something with the window on closing event… I added this in which makes the application exit:

    • Chrissy LeMaire says:

      Ah, yes! This was actually stripped from another script that has a Stop-Process. Thank you for pointing that out, I’ll add it to the code!

  2. Hey Chrissy – first – very nice post!

    You could also show up the powershell console again after the application has been closed:

    # Make PowerShell Disappear
    $windowcode = ‘[DllImport(“user32.dll”)] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);’
    $asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
    $process = Get-Process -PID $pid
    $null = $asyncwindow::ShowWindowAsync($process.MainWindowHandle, 0)

    #Closing the application
    $null = $asyncwindow::ShowWindowAsync(($process).MainWindowHandle, 3)


  3. Doyler says:

    Ah, thanks for that David!

    I ended up commenting out the section that made PowerShell disappear, commenting out the window exit, and switching back to ShowDialog().

    That said, I definitely prefer your method more, so thanks again.

    A note to anyone else looking to copy and paste from the comments section: the comments section currently uses ‘pretty’ quote characters, so you may need to update that code from David on line 2 (it will run regardless, but you’ll get some weird errors)

  4. I think the WPF way of doing this is to use Application.Run:

    $app = [Windows.Application]::new()

    I’ll try doing it that way in ShowUI and see if it helps…

    • Chrissy LeMaire says:

      Ohhh, that seems to work, too, and makes more sense. Let me know! I’ll add it to my SQL monitoring tool, too.

      • Chrissy LeMaire says:

        Interesting. My SQL Monitoring tool has a winform element (notifyicon) and it’s complaining about runspaces and crashing out. I’ll have to look into this more. Looks like pure WPF may be [Windows.Application]::new() and mixtures of WPF/Winforms may be the Appcontext solution.

    • Chrissy LeMaire says:

      So far liking that a lot more. Show() and EnableModelessKeyboardInterop are no longer required :D I never did like Show().

      • David Tawater says:

        Which method finally worked out? I added a change to the closing event.

        # Add Exit (Thanks, Ryan!)
        #Stop-Process $pid


        # Make PowerShell Disappear
        #$windowcode = ‘[DllImport(“user32.dll”)] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);’
        #$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
        #$null = $asyncwindow::ShowWindowAsync((Get-Process -PID $pid).MainWindowHandle, 0)

        I wanted to have a develop mode and a prod mode. did $ eventually work out? if so how did you handle the closing of the application?

  5. Mike says:

    Hello Chrissy,
    I wish I had seen this on Friday so I could spend the weekend playing with all of the really cool stuff in here! :) tytyty. Yep I’m a stoked grommet (surf newb excited to just be in the water).

    I’m pretty new to powershell gui-ing and I’ve been working on manipulating IE windows, so when I saw the Get-Process / Stop-Process I was excited cause I recognized something in an advanced script :D.

    Then I stumbled, I didn’t understand from the script I copied, where the $pid variable is set so that you can use it in both the *-Process commands?

    But with a little snooping I stumbled (gracefully) into the “about_Automatic_Variable”….so cool there’s $pid !!!

    Thank you for this post, after studying it, I’ll have some (more) cool stuff to play with.

  6. Bruno S. says:

    Thanks a lot!, this was very helpful! But how can i interact with a button, i added a button in the setup of the xaml:

    if i use the $window.Content.Children.Name i can see the button
    but i dont know how to use it!!? Help please! thanks :D

    • Bruno S. says:

      i lost this part in my comment:
      XAML button line:
      Button Name=”ButtonTest” Content=”Test” HorizontalAlignment=”Left” Margin=”30,67,0,0″ VerticalAlignment=”Top” Width=”233″ Height=”37″

  7. Muhammad Mashwani says:

    From the comments above, I am seeing the use of Stop-Process -Id $PID to close the hidden PowerShell session at the end so that it is not hanging around. I don’t think this is a good idea as it does not gracefully exit the PowerShell console and you would never get a proper exit code back when you essentially kill the process like that.

    Code to hide or show PowerShell console window:
    If ($Host.Name -eq ‘ConsoleHost’) {
    If (-not ([Management.Automation.PSTypeName]’PowerShellConsole.Window’).Type) {
    Add-Type -Namespace ‘PowerShellConsole’ -Name ‘Window’ -IgnoreWarnings -ErrorAction ‘Stop’ -MemberDefinition ‘
    [DllImport(“kernel32.dll”, CharSet = CharSet.Auto, SetLastError = false)] public static extern IntPtr GetConsoleWindow();
    [DllImport(“user32.dll”, CharSet = CharSet.Auto, SetLastError = false)] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
    public static bool IsPowerShellConsoleHidden = false;
    public static void HideConsoleWindow() { ShowWindowAsync (GetConsoleWindow(), 0); IsPowerShellConsoleHidden = true; }
    public static void ShowConsoleWindow() { ShowWindowAsync (GetConsoleWindow(), 5); IsPowerShellConsoleHidden = false; }
    public static void ShowConsoleWindow() { ShowWindowAsync (GetConsoleWindow(), 7); IsPowerShellConsoleHidden = false; }


    To gracefully exit the hidden PowerShell console, you can use this method of exiting:
    $ExitCode = 0

    If you don’t want to use the above method of exiting, then you can do the following as well to exit gracefully:
    $PowerShellConsoleWindow = Get-Process -Id $PID
    # Have to show the console window to call CloseMainWindow() method. Below method will “show” the window so it can receive the close message but it will not show anything to the user.

  8. Ravi Kant Shivhare says:

    What will be the alternative of:

    $result = $form.ShowDialog()
    if ($result –eq [System.Windows.Forms.DialogResult]::OK)
    #do something

    How we can get the return value i.e. result using:


Leave a Reply