If you’re deploying an application across your organization, that makes use of a notification area icon in Windows 7 or Windows 8, it can be annoying for users when Windows decides to hide the icon. Here we look at a way of forcing notification area icons (system tray icons) to always be visible to the user via Group Policy Objects (GPO) and a little scripting.

Back in Windows 95, the notification area, or system tray, was a bit of a free-for-all. Every application thought that it was worthy of a constant presence in the corner of your screen. While some of these icons are useful, the vast majority are generally not. Windows XP started to address this clutter issue by allowing us to hide/collapse the icons that we didn’t really use or need. Windows Vista removed more of these for us, with Windows 7 going a step further by making most icons here hidden by default, unless specifically permitted by the user to display in the notification area all of the time.

Notification Area alias System Tray

Notification Area alias System Tray

Most of the time, this management of our notification area is fine, however occasionally it can work against you. For example, you might roll out an instant messaging or business application that you would like to always be visible via the notification area. Windows will by default hide this from your users, and doesn’t provide an easy way to automate or script the visibility of these icons for system administrators, as the registry key that stores this information is a long binary key.

Luckily for us, Micah Rowland has spent a while reverse engineering this key, and produced a very handy script that easily allows us to force icons to be visible.

Go over to his site, and copy/paste the script into a new file, and save it as ‘NotifyIcons.ps1’. The next stage is to add the script into a GPO that runs for the users that you want to apply this setting to.

Open up the Group Policy Management MMC. Select your chosen Group Policy Object, right click and select Edit. In the editor, navigate to User Configuration > Windows Settings > Scripts > Logon

Group Policy Management Editor - Logon Scripts

Group Policy Management Editor - Logon Scripts

Once you’re in the login script properties window, move to the PowerShell tab, and click the View Files button. This will open an Explorer window – Move the NotifyIcon.ps1 script we created earlier into this location, so that our GPO login script can see it, you can then close that Explorer window.

In the login script properties window, add a new PowerShell script, in the script name, enter NotifyIcon.ps1, and then in the parameters, enter the program name (case sensitive!) followed by the setting to use:

0 = only show notifications
1 = hide icon and notifications
2 = show icon and notifications

If you’re here and reading this far, you’ll probably want to select setting 2… In my example I enter ‘tftp32.exe 2’ as my parameters.

Add new PowerShell script

Add new PowerShell script

At this point, we can now save and close our GPO’s. Now when users login, the GPO should run our script that will look for tftpd32.exe in the notification area history, then set it to always display.

In order for this to work, the user must have previously run the application, and then properly logged out, so that explorer.exe gets a chance to write the updated notification area history to the Registry. On a subsequent login, our script should successfully locate the program in the history, and update its setting to always show.

If you run into difficulty, ensure that the case of the executable is correct – It does matter! You can also try running the script manually from a PowerShell prompt to debug, but you MUST kill explorer.exe (‘taskkill /f /im explorer.exe’) before running it, otherwise explorer won’t see your update, and will overwrite it when it does quit.

  1. Ajay 9 years ago

    Hi I used this same file to look for the communicator.exe as the parameter file and this does not work for me. I copied the exact same script but it does not work for any exe as I state in the parameter field in the GPO. I have stated "communicator.exe 2" without the speech marks.

    Can you please help?

  2. Author
    Geoff Kendal 9 years ago

    Hi Ajay,

    Have you tried manually running the powershell script - it will give a little output that might help you figure out what's going on.

    Also, remember it is case sensitive.

  3. Armu 9 years ago

    This one really starts to remind me of Raymond Chen's blog: http://blogs.msdn.com/b/oldnewthing/archive/2009/12/22/9939868.aspx

  4. David 8 years ago

    Excellent thanks for this. One question;trying to work out what the default icons filenames are?! Looking to hide the network icon as we have summat else managing stuff, can't find anything!

  5. uzul 8 years ago

    i try to manual because i d'ont need to start with this script when windows start. I don't know where i can write the different value in the script. I am a beginner 🙂
    i would like to run with "pidgin.exe" (application) with the setting "0"

    thanks for the answers

  6. sinan 8 years ago

    The file link is not working.

  7. sinan, thanks for the hint. The link works again now.

  8. Sven 7 years ago

    Hi, this is what I need for my java desktopapp, unfortunate the site with the script is offline.
    Can someone send me with the script, please?
    Thx Sven

  9. Tony 7 years ago

    Hello, I tested the PS script and was able to get it to work properly, but when I try to add it via GPO like you showed above, it doesn't work. Is there something I may be missing?

  10. Jason 6 years ago

    I couldn't get to work via GPO either until I added "Stop-Process -Name explorer" to the bottom of the script. (Even though the command is Stop, explorer starts itself again.)

  11. Stephen, thanks for the hint. I changed the link in the article now.

  12. DeployGuy (Rank: 1)
    4 years ago

    I wanted to have a function with a hard coded value for testing.

    I added the following to the top and bottom of the script.

    At the top

    Function Set-IconVisible{

    At the bottom

    Set-IconVisible nsload.exe 2
    stop-process -name explorer

    In this example I am making the Citrix NetScaler Gateway Plugin icon visible

    The stop-process cmdlet has an interesting behavior when stopping the Windows shell (explorer.exe), that behavior is that it kills the task without letting it write the values in memory which would overwrite the changes made by the script, then Explorer.exe automatically restarts!  And the icon becomes visible immediately!

    The one caveat is I don't believe a non-administrator can kill the Windows Shell so the GPO method is superior. But for testing by and administator this small modification seems to work.

    To use this as an SCCM Package i would use the run a program first option under the user context to change the users tray setting, then use the stop-process -name explorer comand to make it take effect immediatly, should work, i will try to test this and report back.

  13. Chris 4 years ago

    Do we have something for Windows 10 ?


  14. Alessandro Zola 3 years ago

    I too need something for win 10.

    The script doesn't find any program....

  15. Mattias Granditsky 3 years ago

    This works for me in Win10 1803.

    Change ProgramName to whatever the exe in the traybar is called.

    This powershell will set your f5fpclientW.exe Taskbar Icon to always show
    It then restarts explorer so the changes take effect. Your client will
    notice the desktop / menu bar 'flicker' as Explorer restarts.
    Or copy / paste into script

    $ProgramName = "f5fpclientW.exe"
    $Setting = 2
    $encText = New-Object System.Text.UTF8Encoding
    [byte[]] $bytRegKey = @()
    $strRegKey = ""
    $bytRegKey = $(Get-ItemProperty $(Get-Item 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify').PSPath).IconStreams
    for($x=0; $x -le $bytRegKey.Count; $x++)
    $tempString = [Convert]::ToString($bytRegKey[$x], 16)
    0 {$strRegKey += "00"}
    1 {$strRegKey += "0" + $tempString}
    2 {$strRegKey += $tempString}

    [byte[]] $bytTempAppPath = @()
    $bytTempAppPath = $encText.GetBytes($ProgramName)
    [byte[]] $bytAppPath = @()
    $strAppPath = ""

    Function Rot13($byteToRot)
    if($byteToRot -gt 64 -and $byteToRot -lt 91)
    $bytRot = $($($byteToRot - 64 + 13) % 26 + 64)
    return $bytRot
    elseif($byteToRot -gt 96 -and $byteToRot -lt 123)
    $bytRot = $($($byteToRot - 96 + 13) % 26 + 96)
    return $bytRot
    return $byteToRot

    for($x = 0; $x -lt $bytTempAppPath.Count * 2; $x++)
    If($x % 2 -eq 0)
    $curbyte = $bytTempAppPath[$([Int]($x / 2))]
    $bytAppPath += Rot13($curbyte)
    $bytAppPath += 0

    for($x=0; $x -lt $bytAppPath.Count; $x++)
    $tempString = [Convert]::ToString($bytAppPath[$x], 16)
    0 {$strAppPath += "00"}
    1 {$strAppPath += "0" + $tempString}
    2 {$strAppPath += $tempString}

    if(-not $strRegKey.Contains($strAppPath))
    Write-Host Program not found. Programs are case sensitive.

    [byte[]] $header = @()
    $items = @{}

    for($x=0; $x -lt 20; $x++)
    $header += $bytRegKey[$x]

    for($x=0; $x -lt $(($bytRegKey.Count-20)/1640); $x++)
    [byte[]] $item=@()
    $startingByte = 20 + ($x*1640)
    $item += $bytRegKey[$($startingByte)..$($startingByte+1639)]
    $items.Add($startingByte.ToString(), $item)

    foreach($key in $items.Keys)
    $item = $items[$key]
    $strItem = ""
    $tempString = ""
    for($x=0; $x -le $item.Count; $x++)
    $tempString = [Convert]::ToString($item[$x], 16)
    0 {$strItem += "00"}
    1 {$strItem += "0" + $tempString}
    2 {$strItem += $tempString}
    Write-Host Item Found with $ProgramName in item starting with byte $key
    $bytRegKey[$([Convert]::ToInt32($key)+528)] = $setting
    Set-ItemProperty $($(Get-Item 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify').PSPath) -name IconStreams -value $bytRegKey
    ps explorer | kill

    • Jan 1 year ago

      This works fine. I tried several programs and the script works on all of them except one: Gajim
      Output of script is the same everytime - Program not found. Programs are case sensitive.

      I tried few names which can be interpreted as a program name, like: gajim.exe, Gajim.exe'C:\Program Files\Gajim\bin\Gajim.exe'

      I thought that the name of program (for the script) corespond with the name of main binary .exe file like for other programs. For the Gajim apparently not angry

      As a last thing what I noticed is:
      In the Task Manager - Gajim have two processes, Gajim and gdbus.exe. The processes are separately obviously (which you can see in 'Details' tab), but in the 'Processes' tab is merged to a 'Gajim' bundle process. This can be a trouble for script or maybe not.

      Any ideas? Somebody. Anyone laugh


  16. Lodi 3 years ago

    I can't get this to work in our environment with

    "Cannot invoke method. Method invocation is supported only on core types in this language mode."

    M$ = useless tools :@

  17. gab2 3 years ago

    Program not found 🙁

    I tried search for parts of the filename or the directory name and worked

  18. Thomas D 2 years ago

    I used that powershell script no problem with Skype for business 'lync.exe' but it will not work with Teams (Teams.exe) I even decrypted the iconstreams to ensure that Teams was in the list but I still get a message that it's not found. Sort of out of luck.

  19. Marek G 2 years ago

    I was in the same boat as Thomas D. Skype for Business (lync.exe) was working flawlessly, but Teams (Teams.exe) was not - "Program not found" error.

    With trial and error I was able to find out why Teams.exe was not found by the script. There is something wrong in decoding letter "m" in program name. If we skip letter "m" in the name of the program, the program is found. For example when program name is "Tea" or "s.exe" it works for Teams.

    You can even use part of the path where program is located. For Teams it works when you use for example "current" or "/current/" as program name parameter. Hope this helps.

    • Jan 1 year ago

      Thanks Marek. You comment was so interesting for me that I had forced myself to read every comments in this topic and mainly here - which is the origin blog post about script. And believe or not, the fix was there.

      Locate in the script function Rot13 and replace it by this and you're done:

      Function Rot13($byteToRot)
          if($byteToRot -gt 64 -and $byteToRot -lt 91)
              $bytRot = $($($byteToRot - 65 + 13) % 26 + 65)
              return $bytRot
          elseif($byteToRot -gt 96 -and $byteToRot -lt 123)
              $bytRot = $($($byteToRot - 97 + 13) % 26 + 97)
              return $bytRot
              return $byteToRot

      Now the script correctly detects even programs which name contains 'm' or 'M' character.

      By the way: Script correctly works on Windows 10 1909 and also 2004

      PS: With MS Teams (Teams.exe) happened me strange thing. With few first tryes the script cannot found Teams.exe. But maybe on 5th try suddenly catched a program and correctly set tray icon. Maybe it's caused by explorer.exe caching or something, who knows.

  20. Stephen 2 years ago

    How could I get this to work for the Sync Center? Sync Center's process name is Mobsync.exe, but its tied to the explorer.exe process.

Leave a reply

Please enclose code in pre tags

Your email address will not be published. Required fields are marked *


© 4sysops 2006 - 2022


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account