Skip to content

Windows Context Menu Zip

This Powershell script will allow you to add a couple of ZIP options to the Windows right-click Context Menu for Windows folders.

Overview

Save the following script into a folder of your choice, i.e. C:\Tools\Powershell.

MKDIR C:\Tools\Powershell
CD C:\Tools\Powershell

Save the file something like Context-Menu-Zipper.ps1 in your folder.

NOTEPAD C:\Tools\Powershell\Context-Menu-Zipper.ps1

Here is the entire standalone script...

<#
███    ██    ██████    ██   ██    ██████   ██   ██    ██   ██   ███████
████   ██   ██    ██    ██ ██    ██        ██   ██    ██   ██   ██
██ ██  ██   ██    ██     ███     ██        ██   ██    ██   ██   ███████
██  ██ ██   ██    ██    ██ ██    ██        ██    ██  ██    ██        ██
██   ████    ██████    ██   ██    ██████   ██     ████     ██   ███████

 C O N T E X T   M E N U   Z I P P E R
───────────────────────────────────────────────────────────────────────
 Author     : Noxcivis
 Website    : https://www.noxcivis.com
 Version    : 1.0
 Date       : 2025‑07‑29
───────────────────────────────────────────────────────────────────────

.SYNOPSIS
Adds two silent “Zip” actions to the Windows folder context‑menu:
  • Zip (Timestamp)              – instant, no prompt
  • Zip (Timestamp + Suffix…)    – prompts for a suffix and remembers it

.DESCRIPTION
Context Menu Zipper installs lightweight VBS helper launchers that call this
PowerShell script in hidden‑console mode, eliminating window flashes.  It also
stores the last suffix per user (HKCU) and supports full CLI use.

.PARAMETER Setup
Install or refresh the two context‑menu entries and helper VBS files
(admin‑only).

.PARAMETER Uninstall
Remove the context‑menu entries and helper files (admin‑only).
Aliases: -Remove, -Cleanup

.PARAMETER Folder
Folder path passed by Explorer or supplied manually for CLI zipping.

.PARAMETER Suffix
Optional text to append to the ZIP filename (e.g. “V1”).
Ignored for the plain Zip (Timestamp) action.

.EXAMPLE
PS> .\ContextMenuZipper.ps1 -Setup
Installs / refreshes the context‑menu items (run from an elevated shell).

.EXAMPLE
PS> .\ContextMenuZipper.ps1 -Folder 'C:\Project' -Suffix RC2
Creates C:\Project_20250729_142300_RC2.zip via CLI.

.LINK
https://www.noxcivis.com/ContextMenuZipper

.NOTES
Requires: PowerShell 5.1 or later (STA capable)
Admin rights needed only for -Setup and -Uninstall operations.

    LICENSE
    This script is released under an MIT‑style “as‑is” license.

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the “Software”), to deal
    in the Software without restriction—including use, copy, modify, merge,
    publish, and distribute copies of the Software—subject to the following
    conditions:

    1. The above copyright notice and this permission notice shall be included in
       all copies or substantial portions of the Software.
    2. You may not sell the unmodified script as your own product or remove the
       original attribution.

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM,
    OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
#>

param(
    [string]  $Folder,          # supplied by Explorer or CLI
    [switch]  $Setup,
    [switch]  $PromptSuffix,    # internal – tells script to prompt
    [string]  $Suffix,

    # uninstall aliases
    [Alias('Remove','Cleanup')]
    [switch]  $Uninstall
)

# ── paths ────────────────────────────────────────────────────────────────────
$Self       = $PSCommandPath                          # full path to this ps1
$ScriptDir  = Split-Path -Path $Self -Parent
$PlainVbs   = Join-Path $ScriptDir 'ZipPlain.vbs'
$SuffixVbs  = Join-Path $ScriptDir 'ZipSuffix.vbs'

# ╔═ Helper: prompt for a suffix (GUI, falls back to console) ════════════════╗
function Read-ZipSuffix {

    # registry location
    $regPath  = 'HKCU:\Software\Context Menu Zipper'
    $regName  = 'LastSuffix'

    try {
        Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop
        Add-Type -AssemblyName System.Drawing         -ErrorAction Stop

        # ── fetch previously saved suffix (if any) ────────────────────────────
        $last = ''
        try {
            $last = (Get-ItemProperty -Path $regPath -Name $regName -ErrorAction Stop).$regName
        } catch { }

        # ── build dialog ──────────────────────────────────────────────────────
        $form                     = New-Object Windows.Forms.Form
        $form.Text                = 'Zip suffix'
        $form.Width               = 420
        $form.Height              = 150
        $form.FormBorderStyle     = 'FixedDialog'
        $form.MaximizeBox         = $false
        $form.StartPosition       = 'CenterScreen'
        $form.TopMost             = $true

        try { $form.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$env:SystemRoot\System32\zipfldr.dll") } catch { }

        $label            = New-Object Windows.Forms.Label
        $label.Text       = 'Optional suffix (e.g., V1). Leave blank for none:'
        $label.AutoSize   = $true
        $label.Left       = 10
        $label.Top        = 15
        $form.Controls.Add($label)

        $box              = New-Object Windows.Forms.TextBox
        $box.Left         = 10
        $box.Top          = 40
        $box.Width        = 380
        $box.Text         = $last            # ← pre‑populate
        $form.Controls.Add($box)

        $link             = New-Object Windows.Forms.LinkLabel
        $link.Text        = 'Noxcivs'
        $link.Left        = 10
        $link.Top         = 72
        $link.AutoSize    = $true
        $null = $link.Add_LinkClicked({
            Start-Process 'https://www.noxcivis.com/ContextMenuZipper'
        })
        $form.Controls.Add($link)

        $okBtn            = New-Object Windows.Forms.Button
        $okBtn.Text       = 'OK'
        $okBtn.DialogResult = 'OK'
        $okBtn.Left       = 220
        $okBtn.Top        = 70
        $form.AcceptButton = $okBtn
        $form.Controls.Add($okBtn)

        $cancelBtn            = New-Object Windows.Forms.Button
        $cancelBtn.Text       = 'Cancel'
        $cancelBtn.DialogResult = 'Cancel'
        $cancelBtn.Left       = 300
        $cancelBtn.Top        = 70
        $form.CancelButton    = $cancelBtn
        $form.Controls.Add($cancelBtn)

        # ── show dialog ───────────────────────────────────────────────────────
        if ($form.ShowDialog() -ne 'OK') { return $null }

        $newSuffix = $box.Text

        # ── persist the new/cleared suffix ───────────────────────────────────
        try {
            if (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null }
            if ($newSuffix) {
                New-ItemProperty -Path $regPath -Name $regName -Value $newSuffix -PropertyType String -Force | Out-Null
            } else {
                # user left it blank → remove the value if it exists
                if (Get-ItemProperty -Path $regPath -Name $regName -ErrorAction SilentlyContinue) {
                    Remove-ItemProperty -Path $regPath -Name $regName -Force
                }
            }
        } catch { }  # silently ignore registry write errors

        return $newSuffix
    }
    catch {
        try {
            $input = Read-Host "Suffix [$last] (Enter to keep, Ctrl+C to cancel)"
            if ($input -eq '') { $input = $last }
            # update registry from console path as well
            if (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null }
            if ($input) {
                New-ItemProperty -Path $regPath -Name $regName -Value $input -PropertyType String -Force | Out-Null
            }
            return $input
        } catch { return $null }
    }
}

# ╔═ Helper: write two silent VBS wrappers ════════════════════════════════════╗
function Write-HelperScripts {
    param([string]$Dir,[string]$PsPath)

    $psEsc = $PsPath -replace '"','""'   # escape quotes for VB string literal

$plainVbs = @"
' ZipPlain.vbs — silent PowerShell wrapper (no suffix prompt)
Dim sh, folder
Set sh = CreateObject("WScript.Shell")
folder = WScript.Arguments(0)
sh.Run "powershell -NoLogo -NoProfile -Sta -WindowStyle Hidden -ExecutionPolicy Bypass -File ""$psEsc"" -Folder """ & folder & """", 0, False
"@

$suffixVbs = @"
' ZipSuffix.vbs — silent PowerShell wrapper with suffix prompt
Dim sh, folder
Set sh = CreateObject("WScript.Shell")
folder = WScript.Arguments(0)
sh.Run "powershell -NoLogo -NoProfile -Sta -WindowStyle Hidden -ExecutionPolicy Bypass -File ""$psEsc"" -Folder """ & folder & """ -PromptSuffix", 0, False
"@

    Set-Content -Path (Join-Path $Dir 'ZipPlain.vbs')  -Value $plainVbs  -Encoding ASCII
    Set-Content -Path (Join-Path $Dir 'ZipSuffix.vbs') -Value $suffixVbs -Encoding ASCII
}

# ╔═ Core: zip folder with timestamp (+ optional suffix) ══════════════════════╗
function Invoke-Zip-Folder {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateScript({ Test-Path $_ -PathType Container })]
        [string] $FolderPath,
        [string] $Suffix
    )

    $parent    = Split-Path $FolderPath -Parent
    $name      = Split-Path $FolderPath -Leaf
    $timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'

    $clean = if ($Suffix) {
        $bad = [IO.Path]::GetInvalidFileNameChars() -join ''
        ($Suffix -replace "[{0}]" -f ([Regex]::Escape($bad)), '_').Trim()
    }

    $tag     = if ($clean) { "_$clean" } else { "" }
    $zipPath = Join-Path $parent "$name`_$timestamp$tag.zip"

    Compress-Archive -Path (Join-Path $FolderPath '*') -DestinationPath $zipPath -Force
}

# ╔═ Setup: create helper VBS files + registry entries ════════════════════════╗
function Setup-Context-Menus-For-Zip {

    $isAdmin = (New-Object Security.Principal.WindowsPrincipal `
                ([Security.Principal.WindowsIdentity]::GetCurrent())
               ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    if (-not $isAdmin) { throw 'Run -Setup in an *elevated* PowerShell window.' }

    # 1. create helpers
    Write-HelperScripts -Dir $ScriptDir -PsPath $Self

    # 2. add / refresh registry entries
    $root      = 'Registry::HKEY_CLASSES_ROOT\Directory\shell'
    $plainKey  = Join-Path $root 'ZipWithTimestamp'
    $suffixKey = Join-Path $root 'ZipWithTimestampSuffix'
    foreach ($k in @($plainKey,$suffixKey)) { if (Test-Path $k) { Remove-Item $k -Recurse -Force } }

    # ▸ Zip (Timestamp)
    New-Item $plainKey -Force | Out-Null
    New-ItemProperty $plainKey -Name MUIVerb -Value 'Zip (Timestamp)' -Force | Out-Null
    New-ItemProperty $plainKey -Name Icon    -Value 'zipfldr.dll,0'   -Force | Out-Null
    $cmdPlain = 'wscript.exe "' + $PlainVbs.Replace('"','""') + '" "%V"'
    New-Item (Join-Path $plainKey 'command') -Force |
        Set-ItemProperty -Name '(default)' -Value $cmdPlain -Force

    # ▸ Zip (Timestamp + Suffix…)
    New-Item $suffixKey -Force | Out-Null
    New-ItemProperty $suffixKey -Name MUIVerb -Value 'Zip (Timestamp + Suffix…)' -Force | Out-Null
    New-ItemProperty $suffixKey -Name Icon    -Value 'zipfldr.dll,0'             -Force | Out-Null
    $cmdSuffix = 'wscript.exe "' + $SuffixVbs.Replace('"','""') + '" "%V"'
    New-Item (Join-Path $suffixKey 'command') -Force |
        Set-ItemProperty -Name '(default)' -Value $cmdSuffix -Force

    Write-Host '✔  Context menus installed / refreshed.'
}

# ╔═ Uninstall: remove registry entries + helper files ════════════════════════╗
function Remove-ContextMenus {

    $isAdmin = (New-Object Security.Principal.WindowsPrincipal `
                ([Security.Principal.WindowsIdentity]::GetCurrent())
               ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    if (-not $isAdmin) { throw 'Run -Uninstall in an *elevated* PowerShell window.' }

    $root      = 'Registry::HKEY_CLASSES_ROOT\Directory\shell'
    $plainKey  = Join-Path $root 'ZipWithTimestamp'
    $suffixKey = Join-Path $root 'ZipWithTimestampSuffix'

    foreach ($k in @($plainKey,$suffixKey)) {
        if (Test-Path $k) {
            Remove-Item $k -Recurse -Force
            Write-Host "Removed registry key: $k"
        }
    }

    foreach ($file in @($PlainVbs,$SuffixVbs)) {
        if (Test-Path $file) {
            Remove-Item $file -Force
            Write-Host "Deleted helper file : $file"
        }
    }

    Write-Host '✔  Context menus and helper files removed.'
}

# ╔═ Entry point ══════════════════════════════════════════════════════════════╗
try {
    switch ($true) {

        { $Uninstall } {
            Remove-ContextMenus
            break
        }

        { $Setup } {
            Setup-Context-Menus-For-Zip
            break
        }

        { $Folder } {
            if ($PromptSuffix) {
                $tag = Read-ZipSuffix
                if ($null -eq $tag) { break }    # cancel → nothing
                Invoke-Zip-Folder -FolderPath $Folder -Suffix $tag
            } else {
                Invoke-Zip-Folder -FolderPath $Folder -Suffix $Suffix
            }
            break
        }

        default {
            Write-Host @"
USAGE
  Install (admin)     : & '$Self' -Setup
  Uninstall (admin)   : & '$Self' -Uninstall   # or -Remove / -Cleanup

  CLI zipping         : & '$Self' -Folder 'C:\Path\Project' [-Suffix RC1]

  Explorer (after setup):
      ▸ Zip (Timestamp)
      ▸ Zip (Timestamp + Suffix…)
"@
        }
    }
}
catch { Write-Error $_.Exception.Message }

Setup

With administrator rights...

Run

[Your-Folder]\[Script-Name.ps1] -Setup

i.e.

C:\Tools\Powershell\Context-Menu-Zipper.ps1 -Setup

Context Menu Use

Right click on a folder in Windows Explorer and select one of the items shown below.

alt text

Command Line Use

From a Powershell command prompt, run the following command...

C:\Tools\Powershell\ContextMenuZipper.ps1 -Folder "C:\TEMP\Demo"

Suffix

If you want to add a predefined suffix to the output filename, i.e.

-Suffix "V1"

Uninstall

If you want to remove the Context Menus, just run the following, via an administrator Powershell command window.

C:\Tools\Powershell\Context-Menu-Zipper.ps1 -Uninstall

Technical Notes

When you run the script as an administrator using the -Setup switch, two addional VBS files will be created. This is normal and is required to setup the Context Menus.