Windows Command Processor extensions, part 2
A flexible extension system for one of the oldest Windows applications
Published in Articles on by Michiel van Oosterhout ~ 4 min read
Part 1 of this series used Windows Command Processor's AutoRun to ensure our extension system can be initialized, but left the implementation for later, because we first needed to decide how to extend Windows Command Processor. This part 2 of the series explores the use of functions as an extension mechanism.

Commando: a Windows Command Line extension system
PowerShell's module system allows modules to export new cmdlets. We can achieve a similar effect for Windows Command Processor by adding scripts to subdirectories of a directory that is itself added to the PATH
environment variable.
Functions
Windows Command Processor supports functions via the built-in call
command. Call can transfer control of the existing process to a script and return control to the caller after the script exits (using the built-in exit
command with the /b
option) or finishes (when control reaches the end of the script).
The caller can pass parameters to a function. These parameters can be read by the function using %1
through %9
(use the built-in shift
command to read more than 9 parameters). For functions that return a value, the caller must pass the name of the variable (by convention as the last parameter) that will contain the result when control returns to the caller. The function can set this variable by expanding the parameter: set %~3=...
(where the 3rd parameter is the name of the return variable).
The Windows PowerShell script below illustrates this approach by adding a very simple Math extension with 2 functions that return their result in a variable:
# Declare the functions
$scripts = @{
Add = @"
@echo off
setlocal
set /a result=%1+%2
endlocal && if "%~3"=="" (echo %result%) else (set %~3=%result%)
"@
Sub = @"
@echo off
setlocal
set /a result=%1-%2
endlocal && if "%~3"=="" (echo %result%) else (set %~3=%result%)
"@
}
# Ensure the extension's directory exists
$path = "$Env:LOCALAPPDATA\Commando\Math"
$_ = New-Item $path -Force -ItemType Directory
# Add the functions
$scripts.GetEnumerator() | ForEach-Object {
$name = $_.Key
$script = $_.Value
Set-Content -Path "$path\$name.cmd" -Value $script
}
The functions exported by the Math extension can be called from other scripts using the call
command in one of two ways: call %LOCALAPPDATA%\Commando\Math\Add.cmd 1 3 sum
, which provides the name of a variable, the value of which will be set by the function, or for /f %%x in ('call %LOCALAPPDATA%\Commando\Math\Sub.cmd 3 1') do @set remainder=%x
which reads the value from stdout
. The result will be available in %sum%
and %remainder%
respectively.
When %LOCALAPPDATA%\Commando\Math
is added to the PATH
environment variable, the exported functions can be called in an interactive Windows Command Prompt session: Add 1 3 sum
. Note that we do not use call
in an interactive session because according to the documentation
.call
has no effect at the command prompt when it is used outside of a script or batch file
Standard library
Windows Command Processor lacks a standard library, so extension authors may need to implement the same basic private functions. Commando should provide a standard library of functions, ideally in the form of a set of standard extension that are always installed.
Summary
Our extension system for Windows Command Processor is designed for extensions to export functions simply by including a script for each function. Commando should make exported functions readily available on the command line and in scripts. The minimum required initialization for this will be to add each extension to the PATH
environment variable. Functions can then be called from other scripts using the built-in call
command.
Part 3 will explore the use of console aliases as another way to extend Windows Command Processor.
Updates
- Added an example of the syntax to call a function that returns its result via
stdout
.