Published in Articles on by Michiel van Oosterhout ~ 3 min read

Windows Command Processor is the original command-line shell for Windows going back as far as Windows NT. The version that ships in Windows 11 is not much different from the original version due to Microsoft's commitment to backwards compatibility. Although I strongly recommend PowerShell for any new development, it is nonetheless interesting to understand how the original Windows command-line shell works. But due to the limitations of Windows Command Processor, doing any serious scripting inevitably leads down the path of abstraction: the development of some kind of library or set of helper scripts. Let's see what is at the end of this path.

Commando: a Windows Command Processor extension system

Unlike PowerShell and its module system, Windows Command Processor has no built-in extension system, and it seems unlikely that Microsoft will ever add one. So we will just have to create one ourselves, but where do we start?

AutoRun

Windows Command Processor will automatically run the script file specified in the AutoRun value under the HKCU:\Software\Microsoft\Command Processor key in the Windows Registry 1. The Windows PowerShell script below creates and registers a one-line AutoRun script if one was not yet registered, or otherwise adds a line to an already registered AutoRun script:

# Ensure the registry key exists
$key = "HKCU:\Software\Microsoft\Command Processor"
if (-not (Test-Path $key))
{
    $_ = New-Item -Force -ItemType Directory -Path $key
}

# Read or or declare the path to the AutoRun script
$name = "AutoRun"
$cmdPath = Get-ItemProperty -Name $name -Path $key -ErrorAction SilentlyContinue
if ($cmdPath)
{
    # Read the registry value as string
    $cmdPath = $cmdPath.AutoRun.ToString()
}
else
{
    # Declare the path to the AutoRun script
    $cmdPath = "`"%LOCALAPPDATA%\Command Processor\AutoRun.cmd`""
}

# Resolve the path to the AutoRun script
$psPath = & cmd /c "for /f `"delims=`" %i in (`"$($cmdPath.Trim('"'))`") do @echo %~fi"

# Ensure the AutoRun script's parent directory exists
$directoryPath = Split-Path $psPath -Parent
if (-not (Test-Path $directoryPath))
{
    $_ = New-Item $directoryPath -Force -ItemType Directory
}

# Ensure the AutoRun script exists
if (-not (Test-Path $psPath))
{
    $_ = New-Item $psPath -Force -ItemType File
}

# (Re)register the AutoRun script
$_ = New-ItemProperty -Force -Name "AutoRun" -Path $key -PropertyType "String" -Value $cmdPath

# Ensure the AutoRun script calls the Commando script
$content = Get-Content -Path $psPath

# Determine if the code to call the Commando script is already in the AutoRun script
$line = "@if exist `"%LOCALAPPDATA%\Commando\Commando.cmd`" call `"%LOCALAPPDATA%\Commando\Commando.cmd`" init"
$containsLine = $false
$content | ForEach-Object { $containsLine = $containsLine -or ($_.Trim() -ieq $line) }

# Add the code to call the Commando script to the AutoRun script
if (-not ($containsLine))
{
    if ($content -eq $null -or $content.Trim() -eq "")
    {
        # Add the code to an empty AutoRun script
        Set-Content -Path $psPath -Value "$line"
    }
    else
    {
        # Add the code to the end of the AutoRun script on a separate line
        Add-Content -Path $psPath -Value "`r`n$line"
    }
}

The AutoRun script ensures Commando is initialized when Windows Command Processor starts by calling a script and passing init as the first parameter. Before we can implement the initialization we should design our extension mechanism. There are 2 ways we can extend Windows Command Processor: with functions (part 2 in this series of articles) and with console aliases (part 3).

Summary

Windows Command Processor has no built-in extension mechanism, but we may be able to create one ourselves. The first step was to use AutoRun to run a script that calls our script with the init parameter. In the next articles in this series we will use functions and console aliases to actually extend Windows Command Processor.


  1. See the remarks about cmd's /d option. ↩︎