Blog

Windows OS Optimization Essentials, Part 4: Startup Items

By Jake Norman

December 15, 2021 | min

Operating systems can be a lot of work for administrators – work to configure the image, work to install the applications, and work to provide the best user experience possible. As with any software, what is provided to you is what the developer intended, but not necessarily what you want or need for your end users.

This blog series introduces you to Windows® Operating System (OS) optimizations, starting with version 1903. I will attempt to keep these optimizations as environment agnostic as possible. Hopefully, these optimizations will be just as good to administrators of physical machines as to a virtual environment utilizing Nutanix Frame®, Citrix® Virtual Apps and Desktops, or VMware Horizon®.

This series aims to share the seemingly infinite number of ways you can optimize a Windows environment, with something for beginners as well as administrators familiar with optimizations but looking to deliver an even better experience within their environment.

Of course, the optimizations provided in this blog series are intended only as a guide. Be sure to vet any optimizations carefully and test the optimizations described in this series internally before pushing the changes to your production environment.

The first entry covered Active Setup. The second entry covered the Microsoft® Store. The third entry covered Services and Scheduled Tasks. In each case, we discussed what each piece is, how it works, and how to optimize it.

This blog addresses Startup Items, which includes Run and RunOnce Registry Keys, as well as the Startup folder that exists in each user’s profile. More specifically, we will discuss their purpose, how they work, and what can be done with them for the purposes of optimization.

Startup Items for Windows OS have been around since Windows 95. While they have changed over the years in how they are implemented and what can be done with them, each startup item can negatively impact the user experience and result in higher resource utilization than desired.

Future blogs will discuss how to optimize your Windows OS environment through Autologgers and Windows Optional Features.

What are Startup Items?

The term Startup denotes the period after a user has control of a desktop but before all Startup Items are finished running. The biggest area of optimization for Startup Items are Run and RunOnce registry keys, with a few other sections I will explain later.

The Startup Items denoted here are special in that they don’t affect a user’s traditional login, which is the time when the user initiates a connection to the machine – whether physically or through a virtual broker – to the point where the user sees a desktop. While the login time frame is easy to identify, the Startup period isn’t easily capturable.

A quick note: Run and RunOnce registry values exist in user and machine contexts as well as 32-bit and 64-bit operating system contexts. In each scenario, these registry values will run differently with different time frames and in different contexts. A breakdown of these scenarios will be discussed later in this blog. Unless otherwise stated, the Run and RunOnce scenarios discussed in this blog focus on items that specifically affect Startup.

Run and RunOnce registry items are used by application developers to run certain custom items when a user logs onto a computer. The purpose varies, but it could be as simple as starting an application like antivirus or customizing an application that exists for this specific user.

While optimizing Startup Items is important to the end user experience, it doesn’t reduce the user logon time, meaning the time between entering user credentials prior to seeing the desktop.

While previous blog entries, specifically UWP applications and Active Setup, could be considered Startup Items, this blog is specific to the Startup Items noted above.

How do Startup Items work?

The login process to a Windows machine is rather complicated, but I will provide a basic breakdown below. The login process is formally known as an Interactive Session, of which there are three main sections. I then have Startup Items as a fourth section of the login process.

The first section of the login is the Pre-userinit. This section is where Group or Local Policies are run, including non-login scripts.

The second section of the login is the Userinit. This is where logon scripts are run and network connections are made. The last step here is to kick off Explorer.exe.

The third section of the login is the Shell. The Shell section starts when Explorer.exe is fully running and is complete when the user receives keyboard and mouse control and, lastly, sees the desktop.

Startup is the period after the user sees the desktop during which all Startup Items start running, including applications that load into your System Tray or the processes you see starting within your Task Manager. Due to this application load, this time frame is the most heavily constrained in terms of resources, as the OS is trying to run quite a few applications at once, but also where the user attempts to start their own applications. Figure 1 below showcases this login path.

Figure 1

Following the above diagram, one thing that stands out here is when the Startup Items run. As opposed to some other optimization efforts, Startup Items happen after the user profile load, whether initial or subsequent. This item is covered later in the blog.

So that explains the login process at a high level. But what about the items inside Startup? Run and RunOnce registry keys, the most common Startup Items, are usually application specific entries that exist in multiple locations based upon a couple variables. Once Startup begins, all Startup Items begin to run simultaneously, usually invisible to the user.

The difference between Run and RunOnce Startup Items is fairly subtle, and it can become very muddled when you include a VDI or DaaS scenario into the mix.

Run values execute each time a user logs into a machine. Since they run each time, there is no difference in how they execute between a non-persistent and persistent scenario.

RunOnce values are designed to execute only during the first login of that user after the values are created, usually due to application installation. However, this situation has caveats based on the environment you are in.

In a persistent scenario, RunOnce values execute as they are supposed to. In a non-persistent scenario, RunOnce values execute at the first user login, after which the OS deletes these values so the values are not executed again (“Run Once”). Due to the nature of non-persistent workloads, the non-persistent machine reverts back to the previous known good state upon reboot, which results in the RunOnce values reappearing the next time a user logs in. This process is shown in Figure 2 below.

Figure 2

What this tells us is that in a non-persistent scenario, Run and RunOnce Startup Items effectively work the same way; each will execute during every login. Because Run and RunOnce Startup Items run simultaneously with every login, some users might complain about Outlook slowness when they first login, but if they wait 5-10 minutes, Outlook opens just fine.

I call it “dead time” when the OS is running all the Startup Items. In this time frame, the user session can run significantly slower than normal due to the sheer number of applications being started at once, attributing to dead time.

Any Startup Item can cause increased resource utilization and a degraded end-user experience. This is most obvious with security applications, such as antivirus. The Startup Item launches the application, which starts a scan of the system.

Unlike Active Setup, each location Run or RunOnce values exist are completely separate and the placement of the values is solely dependent on the application requirements and not the functionality of the underlying operating system.

What does this mean for me?

Like Active Setup and the Microsoft Store, it’s important with Startup Items to understand the differences in how your environment is built. Since a large portion of Startup Items are application-specific entries, it’s vital to understand where each application places items so you understand what you are optimizing.

As a quick guide for the sections below, HKLM is shorthand for the HKEY_LOCAL_MACHINE registry hive. HKLM Run or RunOnce values run in the machine context and they run during boot rather than during a user login. These items don’t generally affect user experience.

HKCU is shorthand for the HKEY_CURRENT_USER registry hive. HKCU Run or RunOnce values run in the user context and they run during user login. Any values in this location only run for a specific user when that logs in, although it is possible for values to exist for other users on the computer.

Additionally, the WOW6432Node registry location is significant because all registry keys and values kept in this location are specific to 32-bit applications that are running on a 64-bit operating system. A 32-bit operating system will not have the WOW6432Node registry key because there is no difference between the bit architecture of the application and the OS.

For instance, HKCU\Software\WOW6432Node\Windows references a 32-bit user registry location while HKLM\Software\Windows references a 64-bit machine registry location. An example is shown below in Figure 3, denoting this breakdown.

Figure 3

Keeping that in mind, let’s delve into the Startup Items with a little more detail. Let’s get down and dirty with how to identify where and what to look for when it comes to Startup Items.

Run registry keys

You need to visit multiple locations in the registry to view your Run registry keys. As previously mentioned, there are different scenarios for where Run and RunOnce are utilized by an application, based upon whether it runs in a machine or user context and whether it is a 32-bit or 64-bit application. The locations you will need to check are shown below. It is possible that some of these locations don’t exist, depending on whether you have applications that use a particular scenario that requires these locations.

HKLM\Software\Microsoft\Windows\CurrentVersion\Run

HKLM\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run

HKCU\Software\Microsoft\Windows\CurrentVersion\Run

An example of one Run location is shown below.

Figure 4 – HKLM 64-bit Run key

But what does this information mean? The Name field shown above is an important registry value. We will utilize this value through scripting to remove Run items you wish to optimize. For example, the Type field can be ignored. The Data field is very important, as that is the exact command that is being run by the OS. Since this is an HKLM location, these entries are run during boot and not during user login.

Run registry items are visible in GUI form as well. If you open the Task Manager application in Windows, there is a Startup tab, which corresponds to some items listed in the Run registry locations. While this view doesn’t show what’s actually running during Startup, it does provide a contextual information that might be useful.

Figure 5

There are two critical things you can see in the GUI that aren’t easily visible elsewhere. One is the Startup impact. This shows what the OS believes to be the impact on the user experience during Startup. The second is the Status, which shows whether the Startup item is enabled or disabled.

You can disable Startup items through this interface. If you right click on any item in this menu, you have the option to disable the Startup Item. Being enabled or disabled has no effect on the corresponding Run value in the registry, although if disabled, the OS will not execute the command in the data field of the value.

That’s because the items in this GUI correspond to different registry locations than what’s stated above. HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved corresponds to machine context Run registry items. Unlike previous locations mentioned in this blog, the alternate registry keys for 32- or 64-bit applications are located in the StartupApproved registry key, called Run and Run32.

The Run registry key in the StartupApproved location should closely match what exists in HKLM\Software\Microsoft\Windows\CurrentVersion\Run, while the Run32 registry key should closely match HKLM\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run. An example is shown below.

Figure 6

There is a matching StartupApproved registry location in HKCU as well, of which the Run sub-key corresponds to the Run registry items HKCU\Software\Microsoft\Windows\CurrentVersion\Run or HKCU\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run location, depending on the existence of any values in those latter two locations.

RunOnce registry keys

RunOnce registry keys live in almost the same places that Run registry keys do. For full clarification, the locations you need to check are denoted below. As with Run registry keys, it is possible that some of these locations don’t exist, depending on whether you have any applications that use a particular scenario that requires these locations.

HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce

HKLM\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\RunOnce

HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce

HKCU\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\RunOnce

An example of one RunOnce location is shown below.

Figure 7– HKCU 64-bit RunOnce key

The purpose of the field in a RunOnce location is the same as with a Run location. Name states what it is. Data states what it executes. As you can see with the RunOnce location shown above, the purpose of the data field is drastically different. Instead of starting applications, these are running scripts, denoted by the cmd.exe in the data field.

Unlike Run registry items, RunOnce items are transient in nature and don’t have a GUI form to view them in. They can only be seen in the registry.

Startup Folder

The last piece of the Startup puzzle is the Startup folder. For those that have been around for a while, yes, this method of implementing a Startup Item still exists, although it’s rarely used these days.

The Startup folder is a user-specific folder that exists in each user’s AppData folder structure. The specific location is C:\Users\(username)\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup. If you are logged in as the user you want to check, you can type %AppData%\Microsoft\Windows\Start Menu\Programs\Startup to get there.

Icons that exist in this folder structure act like Run registry items. Each time a user logs in, the information shown in the Target field contained within the Shortcut will execute, effectively running an application or script. A common one is the Send to OneNote Startup Item, which runs "C:\Program Files\Microsoft Office\root\Office16\ONENOTEM.EXE" /tsr as the executable.

Startup Items that exist in the Startup Folder are analogous to Run values in that they run each time a user logs in. As such, optimizing these items can be important to your user experience.

How to optimize Startup Items in your environment

As opposed to previous blog entries, Startup Items are very specific to each environment. I will provide commands to run, but they will require additional information for your environment that I will not have.

Run and RunOnce registry keys

Removing the registry value that exists in the Run or RunOnce registry locations prevents these values from executing. The PowerShell command for doing so is fairly straightforward: Remove-ItemProperty.

You then set the location of the value you wish to remove using the -path parameter. This makes the command Remove-ItemProperty -path (Path). This parameter is dependent upon the location of the specific value you want to remove. For example, Remove-ItemProperty -path “HKCU:\Software\Microsoft\Windows\CurrentVersion\Run”.

Next, identify the value you wish to remove utilizing the -name parameter. The full command is now Remove-ItemProperty -path (Path) -Name (Value). The name parameter is dependent upon the applications that are installed and have values located in this path. An example of this is the Zoom application, with a command of Remove-ItemProperty -path HKCU:\Software\Microsoft\Windows\CurrentVersion\Run” -Name “Zoom”.

Another way to optimize specific Run registry items is to disable them. This process is a bit more complicated than just removing the Run registry value, but allows for the existence of the registry item in cases where you need to disable/enable on demand.

The registry values kept in either HKLM or HKCU Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved in either the Run or Run32 sub-keys are binary keys.

Any item that is enabled in the Startup tab of the Task Manager will have a binary key that reads “02 00 00 00 00 00 00 00 00 00 00 00” (02 followed by 11 pairs of zeros). While that entire string is important to the OS, for us the focus is on the “02” pair at the beginning, which means that the Startup Item is enabled.

Figure 8

A disabled Startup Item has a value of “03 00 00 00 ## ## ## ## ## ## ## ##” (03 followed by 3 pairs of zeros followed by 8 pairs of random characters). Again, the important piece here is the first pair.

Figure 9

A quick segue way here. While I identify “02” and “03” in these binary keys as enabled and disabled, respectively, that’s not the full limitation. Using “04” in this first pair enables the Startup Item, while “05” disables it. So it’s “even” and “odd” that are the differences between enabled and disabled. That said, using any numbers other than “02” and “03” in the first pair can cause weird issues with how the registry values are set after opening up the Task Manager. My recommendation is to stick with “02” and “03”.

Accomplishing this via PowerShell is a fairly easy command. Unlike deleting a registry value, we want to set a value, for which you would use Set-ItemProperty. The remainder of the command structure is the same. You must provide a Path, a Name and, additionally, a Value. The full command would be Set-ItemProperty -Path (Path) -Name (Name) -Value ([byte[]](0x03)). To re-enable disabled items, use Set-ItemProperty -Path (Path) -Name (Name) -Value ([byte[]](0x02)) instead.

Optimizing items in the Startup Folder

For items you wish to optimize inside the Startup Folder, you must delete the icon that you wish to prevent running. There is no way to disable or enable these items. If the icon exists, the action will run.

Accomplishing this via PowerShell is fairly easy. While you would use the Remove-Item command to specify the shortcuts you wish to prevent from running, you must account for the variable of the user location. The full command would be Remove-Item -Path (Path & File). Therefore, using the Send to OneNote example covered earlier, you would type Remove-Item -Path “$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\Send to OneNote.lnk”.

Potential downsides to optimizing Startup Items

As with any potential optimization, you must decide whether the optimization will help or hurt your environment. For example, if a Run value starts an antivirus application, removing that value will prevent the application from running, which could cause problems.

On the other hand, if an application utilizes a Run value you don’t want in your environment, removing the offending key will optimize the environment to your needs. For example, if an application is configured counter to the way you wish the application to be configured. Simply identify which items are best to optimize and take appropriate action.

One action to optimize Run or RunOnce registry key items is to delete the value itself. However, if you delete something you need later, recreating that value can be troublesome. Installing the application to another location will create the same registry values, which you can use as a baseline to recreate in your image. As with all changes to a production environment, I always recommend documenting changes as you make them, in case you need to roll back those changes.

Conversely, disabling Startup Items that are shown in the Task Manager is a more efficient method of optimizing them, although this only affects Run tasks.

Startup Items that can be optimized

Since Startup Items are specific to applications within your environment, and very rarely denoted by the Windows OS itself, there is little in the way of guidance on what can be optimized, only where to look for potential optimizations and how to affect change.

Additional tips

To easily travel between registry keys that exist in the same location within both the HKLM and HKCU registry hives, right click the key and choose “Go to HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER”.

Figure 10

Previous blog posts provided alternate, easier, ways to script optimizing aspects of your OS using Arrays and ForEach commands. While this is possible with Run and RunOnce items, it’s more complicated because there are two variables in each optimization rather than one: the path and the name parameters.

Because of this, shown below are two easier methods to script Run and RunOnce item optimization, each with their pros and cons.

Method 1:

Due to the existence of a secondary variable, we need two arrays instead of one. Luckily, the first array is straightforward, as there are a limited number of paths to worry about.

$paths = @(

“HKLM:\Software\Microsoft\Windows\CurrentVersion\Run”,

“HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce”,

“etc.”

)


The second array is based on the Name parameter, which is specific to your environment.

$names = @(

“32Bit Run Value 1”,

“32Bit Run Value 2”,

“64Bit RunOnce Value 1”,

“etc.”

)


You then need nested ForEach commands to run through each array. To make this separation clear, I have indented the command.

ForEach ($path in $paths){

         ForEach ($name in $names){

                     Remove-ItemProperty -path $path -name $name

         }

}


The pro here is that you can create a single set of commands to run through each variable in each parameter in a way that covers them all very easily. The con here is that this command is a bit messy. Each path variable will try to utilize each name variable, prompting non-terminating errors in your output because not all names will exist in each path.

A clean yet complex way to handle this involves hard setting the path and having multiple name arrays.

$names1 = @(

“32Bit User Run Value 1”,

“32Bit User Run Value 2”,

“etc.”

)

$names2 = @(

“64Bit Machine RunOnce Value 1”,

“64Bit Machine RunOnce Value 2”,

“etc.”

)

$names3 = etc.

$names4 = etc.


You would then use a ForEach command for each array specified.

ForEach ($name1 in $names1){

Remove-ItemProperty -path “HKCU:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run” -name $name1

}

ForEach ($name2 in $names2){

Remove-ItemProperty -path “HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce” -name $name2

}


The advantage in this example is a cleaner script. There are no errors because there are no incorrect paths or values. But this comes at the expense of a much bigger and harder-to-manage script.

There are tons of different ways to accomplish the same thing using scripting and most PowerShell gurus might have better methodologies. Don’t be afraid to look for other options to achieve what you set out to do.

What’s next?

This post covers Startup Items as they relate to OS optimization, but Startup Items, specifically Run and RunOnce registry keys, can be used by any developer as part of an application deployment. I recommend checking all Startup Items periodically and after application installation to verify which new entries have or have not been added. If they have, vet them for purpose and then remove or keep them as needed.

The next blog post will discuss Autologgers and Optional Features. These areas of the OS correspond more to reducing the density of a virtual environment rather than affecting the end user experience. If you need assistance in optimizing your VDI or DaaS images, check out our Xpert Services Template Image Creation and Optimization service.

Jake Norman is a Senior Consultant with Nutanix Professional Services. He has been a part of the Professional Services team for two years and prior to that spent 15+ years in a myriad of different environments focusing on EUC and UX work.

© 2021 Nutanix, Inc. All rights reserved. Nutanix, the Nutanix logo and all Nutanix products, feature names mentioned herein are registered trademarks or trademarks of Nutanix, Inc. in the United States and other countries. All other brand names mentioned herein are for identification purposes only and may be the trademarks of their respective holder(s). This post may contain links to external websites that are not part of Nutanix.com. Nutanix does not control these sites and disclaims all responsibility for the content or accuracy of any external site. Our decision to link to an external site should not be considered an endorsement of any content on such a site. Certain information contained in this post may relate to or be based on studies, publications, surveys and other data obtained from third-party sources and our own internal estimates and research. While we believe these third-party studies, publications, surveys and other data are reliable as of the date of this post, they have not independently verified, and we make no representation as to the adequacy, fairness, accuracy, or completeness of any information obtained from third-party sources.

This post may contain express and implied forward-looking statements, which are not historical facts and are instead based on our current expectations, estimates and beliefs. The accuracy of such statements involves risks and uncertainties and depends upon future events, including those that may be beyond our control, and actual results may differ materially and adversely from those anticipated or implied by such statements. Any forward-looking statements included herein speak only as of the date hereof and, except as required by law, we assume no obligation to update or otherwise revise any of such forward-looking statements to reflect subsequent events or circumstances.