ClipDiff is a Windows notification-area utility that compares the last two text values captured after it starts. A value can come from copied Unicode text or from a file copied in Explorer.
ClipDiff listens to the Windows clipboard directly. No data is ever uploaded.
- Start
ClipDiff.exe. It opens in the notification area - Copy the older text or file. Explorer's Copy as path also counts as copying the file.
- Either copy the newer text or file, or right-click the newer file in Explorer and choose Compare with current ClipDiff capture.
- If you copied the newer value normally, press
Ctrl+Alt+D(or your chosen replacement) or right-click the ClipDiff icon and choose Show Diff. The Explorer command opens the diff immediately. - You can also copy two files at the same time—or select exactly two files in Explorer and choose Compare two selected files with ClipDiff—to diff them immediately without an existing capture.
The built-in viewer is the default. Its reusable window opens in 'Side by Side' mode but can switch to 'Unified', copy the unified difference as ordinary Unicode text, or clear the captured values. Closing the window hides it; Quit ClipDiff in the notification-area menu exits the application.
The menu's Diff viewer submenu lists supported programs found on the machine, provides Choose program... for another executable, and lets you return to the built-in viewer. The selection is remembered. Keyboard shortcut... opens a small recorder: click the capture box, press a combination containing Ctrl or Alt (with optional Shift), then choose Save. ClipDiff tests the replacement before releasing the current shortcut, reports conflicts in the recorder, and provides Reset to Ctrl+Alt+D. Windows-key shortcuts and Alt+F4 are not accepted.
The menu also lets you pause/resume monitoring, shows short previews of the current and previous entries, and provides an About ClipDiff window with project links. A file-backed entry always includes its filename before the preview. Resuming starts from the clipboard's then-current sequence and does not import text copied while paused. If another application owns the configured shortcut, ClipDiff continues to work through its notification-area menu and labels the command Show Diff (shortcut unavailable).
When Explorer places files on the clipboard, ClipDiff checks the same privacy markers used for ordinary text before obtaining any file paths. Explorer Copy as path is also treated as a file copy when its text consists solely of one or two quoted absolute Windows paths. This deliberately favors the useful file-diff workflow over the unusual case of comparing the quoted paths themselves. Unquoted paths and paths embedded in other text remain ordinary text. A single copied file is converted as follows:
.bat,.cmd,.ps1, terminal captures containing ANSI escape sequences, and other files whose bytes look like text contribute their full decoded contents. UTF-8, BOM-marked UTF-16/UTF-32, common BOM-less UTF-16, and Windows-1252 text are supported.- Known binary executable/package types such as
.exe,.com,.dll, and.msicontribute the filename with a reason, such asprogram.exe (binary file). - Other binary-looking, empty, missing, unreadable, directory, or larger-than-16-MiB entries also contribute the filename with
(binary file),(empty file),(file not found),(file unreadable),(directory), or(file too large)appended as appropriate.
The decision uses both safe binary-extension handling and content inspection, so a binary renamed to .txt still falls back to its filename.
ClipDiff retains the source basename separately from the converted text. The filename is always shown on the corresponding side of the built-in diff, in unified/copied diff headers, in the notification-area current/previous item, and in external-viewer labels. If both sides have the same filename but come from different paths, ClipDiff adds only enough parent directories to distinguish them—for example, branch-a/src/settings.json and branch-b/src/settings.json.
The full source paths are retained only in the same two-entry in-memory history as the captured values so ClipDiff can calculate those labels. The diff document keeps the resolved labels but drops the original paths. Paths are never logged, stored in settings or the Explorer registration, or reproduced in the external-viewer temporary workspace. Evicting or clearing an entry, or exiting ClipDiff, discards its retained path.
When exactly two files are copied together, ClipDiff converts each one independently and immediately uses them as the comparison pair: the first clipboard path is Previous and the second is Current.
While clipboard monitoring is active, selecting exactly two files in Explorer also offers Compare two selected files with ClipDiff. It converts both files with the same rules, atomically replaces the current comparison pair, and opens the configured viewer. Explorer's first supplied path becomes Previous and its second becomes Current. No prior capture is needed, and the Windows clipboard and ClipDiff's clipboard sequence baseline are unchanged.
After ClipDiff has captured at least one value, and while clipboard monitoring is active, it adds Compare with current ClipDiff capture to the Explorer context menu for individual files. If the current capture came from a file, the command appends that filename so the comparison source is visible before it is invoked. Choosing it reads the selected file with the same conversion rules, promotes that result to Current, moves the former Current entry to Previous, and immediately uses the normal Show Diff workflow. It does not change the Windows clipboard. On Windows 11, this classic context-menu command may appear under Show more options.
Both context-menu registrations are per-user, require no administrator access, and use classic Explorer verbs, so Windows 11 may place them under Show more options. The two-file command appears only when exactly two files are selected and ClipDiff is running and monitoring; the individual-file command additionally requires a usable current capture. Clearing removes only the individual-file command, while pausing or quitting removes both. A later ClipDiff start cleans up registrations left by an abnormal exit.
The two-file command uses a small native ClipDiff.ShellExtension.dll beside the executable. Its Shell initialization interface receives the complete selection, so it can show the command only for two files. It checks Shell metadata without reading file contents, then forwards the selection data object to ClipDiff's existing out-of-process COM drop target when clicked. Selected paths never appear in a process command line or the registry. The DLL holds the selection only for the menu's lifetime; file conversion and comparison run in ClipDiff. A memory-only readiness event keeps cached menus inactive while monitoring is paused or ClipDiff has exited.
Copies containing more than two files are ignored; ClipDiff never creates a comparison value from a file list.
File contents and paths are never written by these built-in workflows; the resulting value or pair follows the same two-entry, in-memory history policy as copied text. For the individual-file command, Explorer necessarily supplies the directly selected file path to a short-lived ClipDiff process. ClipDiff forwards it to the existing tray process over a same-user, per-session local pipe. For the two-file command, Explorer marshals the selection directly to the running ClipDiff process. In both cases, each path is retained only with its in-memory entry for collision disambiguation; it is never logged or persisted.
ClipDiff has command-line profiles for these developer tools:
- SourceGear DiffMerge
- WinMerge
- Meld
- KDiff3
- Beyond Compare
- Araxis Merge
- Visual Studio Code
- Visual Studio
- TortoiseGitMerge
- TortoiseMerge
- P4Merge
- ExamDiff Pro
ClipDiff checks Windows App Paths, PATH, and the programs' usual install locations. A program selected with Choose program... is matched to a known profile by executable name; an unknown executable receives the previous and current file paths as two positional arguments. If no external viewer is selected, its executable is no longer available, or it cannot be started, Show Diff falls back to the built-in viewer.
ClipDiff captures only future, non-empty text values (including the copied-file conversion above) and retains at most two accepted values in process memory. Consecutive copies of identical text are accepted as separate entries, allowing ClipDiff to report No differences. Unsupported non-text clipboard changes do not alter the entries. Clear Captured Text removes the in-memory entries and active diff without changing the Windows clipboard. ClipDiff never logs, uploads, or transmits captured text or file contents.
The built-in viewer keeps captured text in memory only. An external program cannot compare in-memory strings directly, so selecting an external viewer creates an explicit exception: after a one-time warning is accepted, ClipDiff writes the two values as UTF-8 plaintext files in a unique directory below %LOCALAPPDATA%\ClipDiff\Temp. Ordinary text uses Previous clipboard.txt and Current clipboard.txt; file-backed values use their source basename within separate Previous and Current child directories so even positional-only viewers expose the filename. The files are marked read-only. ClipDiff attempts to remove that directory after the launched process exits, when ClipDiff exits, and during its next startup.
This cleanup is best effort. A crash, power loss, open file handle, or external viewer that hands work to another process can leave files behind, and the selected program may cache or retain its own copy outside ClipDiff's control. Do not select an external viewer when this disk exposure is unacceptable. Cancelling the warning opens the built-in viewer without writing the files.
ClipDiff stores only the selected executable path, the one-time-warning acknowledgement, and the chosen shortcut's modifier/key codes in %LOCALAPPDATA%\ClipDiff\settings.json; clipboard text and previews are never stored there. Rebuilding or replacing the executable does not reset the acknowledgement. To retest the notice, close ClipDiff and set PlaintextWarningAcknowledged to false in that file; the selected program can remain unchanged. With the built-in viewer, normal exit loses all captured content. With an external viewer, normal exit also attempts to remove every temporary comparison directory.
Before reading text, ClipDiff inspects and honours these advisory clipboard formats:
ExcludeClipboardContentFromMonitorProcessingCanIncludeInClipboardHistorywhen its DWORD is zeroCanUploadToCloudClipboardwhen its DWORD is zero
Malformed or unreadable privacy markers are excluded conservatively. Copy unified diff writes its Unicode text and all three exclusion formats in one native clipboard operation, and ClipDiff suppresses the resulting clipboard update.
Some password managers clear an unmarked value shortly after copying it. If an accepted value is immediately followed by an explicit clipboard clear within 60 seconds, ClipDiff removes that current entry. An intervening unrelated, sensitive, or failed clipboard observation cancels this eligibility. This is only a best-effort heuristic and does not come with a guarantee.
Important limitations:
- Copying a text-like file intentionally causes ClipDiff to read that file from disk and retain its decoded contents in process memory.
- A file-backed entry's full source path is retained in memory until that entry is evicted, cleared, or lost on exit; only the shortest distinguishing suffix is displayed when filenames collide.
- Passwords without standard privacy markers are indistinguishable from ordinary text.
- The automatic-clear heuristic cannot identify every sensitive value or clearing pattern.
- .NET strings cannot be guaranteed to be securely zeroed in memory.
- Operating-system paging, process dumps, other clipboard monitors, and Windows clipboard history are outside ClipDiff's control.
- Temporary plaintext and any copies retained by a selected external diff program are outside the memory-only guarantee described above.
ClipDiff does not guess whether text is sensitive from its length, punctuation, entropy, apparent source process, or token-like appearance.
- Windows 10 version 1809 or newer, or Windows 11
- Windows Server 2022 with Desktop Experience
- An interactive desktop session with a clipboard and notification area
Windows Server Core is not supported. The initial release target is win-x64.
-
Install the .NET 10 SDK (not only the .NET Desktop Runtime) from the .NET 10 download page. Alternatively, install it through Visual Studio Installer by selecting the .NET desktop development workload in a Visual Studio version that supports .NET 10.
-
Open a new PowerShell window after installation and confirm that a
10.0.xSDK is available and selected:dotnet --list-sdks dotnet --version
If
dotnet --versiondoes not start with10., update/install the SDK before continuing. Installing only a runtime is not sufficient to build ClipDiff. -
Clone the repository (or open PowerShell in an extracted source directory), then change to the directory containing
ClipDiff.Windows.sln.
Restore packages, run all automated tests, and compile a Release build:
dotnet restore ClipDiff.Windows.sln
dotnet test ClipDiff.Windows.sln --configuration Release --no-restore
dotnet build ClipDiff.Windows.sln --configuration Release --no-restoreThe framework-dependent executable is then at
src\ClipDiff.Windows\bin\Release\net10.0-windows10.0.17763.0\ClipDiff.exe.
It requires the .NET 10 Windows Desktop Runtime on the computer where it runs.
For a portable executable that includes its runtime, use the local release script
below instead.
The pure ClipDiff.Core project and both policy test assemblies target ordinary net10.0, so they can run on macOS:
dotnet test tests/ClipDiff.Core.Tests/ClipDiff.Core.Tests.csproj
dotnet test tests/ClipDiff.Windows.Tests/ClipDiff.Windows.Tests.csprojThe Windows project has EnableWindowsTargeting=true, which permits cross-compilation where Microsoft targeting packs are available. A successful macOS compile is not a functional Windows test. Clipboard formats, notification-area behaviour, global hotkeys, WPF presentation, and native cleanup still require Windows verification.
Build the native menu extension on Windows with Visual Studio C++ build tools (the Desktop development with C++ workload and a Windows SDK):
./scripts/build-shell-extension.ps1 -Test
dotnet build ClipDiff.Windows.sln --configuration ReleaseThe .NET build copies the built DLL beside the application. The native tests ask Windows to construct the context menu using real Shell selections, then verify visibility and COM delivery. They use temporary registry keys and test files, and require ClipDiff to be closed. Run the build/release scripts from a normal, non-administrator PowerShell window. If launched as administrator with UAC, the tests restart using Windows' linked unelevated token; on CI accounts without a linked token, they create a restricted test child. Both the launch token and child are checked for elevation before Shell tests run. They do not change UAC or account settings. The two-file Explorer menu is unavailable when the DLL is missing; clipboard and tray comparison remain available.
An older test launcher could print elevated=0, elevationType=2 after dropping privileges. Type 2 still identifies a full UAC token, so that child did not reproduce a normal unelevated Shell process. The linked-token restart must report elevated=0, elevationType=3. If an older build fails menu discovery in the former state, rerun from non-administrator PowerShell or rebuild with the corrected launcher.
If the native test run fails, include the full output from Native build: through the final [FAIL] line and script error. The output identifies the test case, expected visibility, direct-handler and Windows-aggregate results, HRESULT/Win32 errors, selection counts and Shell attributes, and effective COM/menu registration. A visibility mismatch also prints menu IDs, states, separator labels, and a read-only snapshot of Shell restriction settings and ClipDiff's Approved/Blocked entries. These probes do not change policy or read clipboard contents or selected file paths. Passing the direct-handler check but failing the Windows-aggregate check narrows the problem to Shell discovery/aggregation; it does not by itself establish that a work-machine policy is responsible. Microsoft's Shell extension approval policy documentation explains one possible restriction.
GitHub Actions also builds and tests the native DLL, then tests and builds the .NET Release solution on windows-latest for every push and pull request. Each successful run provides a ClipDiff-win-x64 artifact containing the executable and native DLL; extract both into the same directory. It can be run manually from the repository's Actions tab as well. Actual Explorer desktop interaction remains a separate manual check.
On Windows PowerShell, from the repository root:
.\scripts\create-local-release.ps1The script builds/tests the native extension and publishes a self-contained, untrimmed win-x64 application to the gitignored releases/win-x64 directory. Distribute both ClipDiff.exe and ClipDiff.ShellExtension.dll together; the .NET portion is bundled into the executable. Pass -Launch to start it after publishing. The personal build is unsigned, so Windows SmartScreen may warn before first launch.
To skip the native Explorer integration tests for a local release, pass -SkipNativeTests:
.\scripts\create-local-release.ps1 -SkipNativeTestsThis still builds and packages the native DLL and runs the .NET tests. Native tests run by default and remain enabled in CI. You can combine this switch with -Launch.
Quit ClipDiff before upgrading. Explorer may keep the DLL loaded; use a new release directory if Windows refuses to replace it, and restart Explorer or sign out if an old handler remains loaded. Starting the new version removes the obsolete two-file static menu registration automatically.
If PowerShell reports that script execution is disabled, allow the checked-out script for this process only, then run it again:
Set-ExecutionPolicy -Scope Process Bypass
.\scripts\create-local-release.ps1The resulting portable executable is releases\win-x64\ClipDiff.exe; copy that
file to the work PC and run it without installing a separate .NET runtime.
No installer, automatic updater, or code signing is included in the initial release.
On a Windows desktop, verify:
- only one notification-area icon appears and a second process exits cleanly;
- clipboard text that existed before startup is not captured;
- two future text copies, including identical text, or one exact-two-file copy produce Ready to diff and
Ctrl+Alt+Dopens the window; - side-by-side rows remain aligned, unified output is exact, and long lines wrap;
- copied and directly selected files show their source filename in the tray item and on the corresponding diff side, including unified output and external viewers; equal filenames from different paths use the shortest unique suffix;
- the built-in viewer is selected by default, detected external programs appear under Diff viewer, and Choose program... accepts another executable;
- selecting an external viewer shows the privacy warning once; cancelling uses the built-in viewer without creating plaintext files;
- each supported installed viewer receives the previous/current sides in the right order and with the documented labels and read-only switches where supported;
- external comparison files appear only below
%LOCALAPPDATA%\ClipDiff\Temp, contain the exact text, and are removed after the launched process exits, on ClipDiff exit, or on the next start; - removing or renaming the selected viewer executable causes Show Diff to fall back to the built-in viewer;
%LOCALAPPDATA%\ClipDiff\settings.jsoncontains only the executable preference, warning acknowledgement, and shortcut codes, never clipboard content;- Keyboard shortcut... records a replacement such as
Ctrl+Alt+6, keeps the old shortcut active when the replacement is already in use, updates the tray label after success, persists across restart, and resets toCtrl+Alt+D; - Copy unified diff pastes into Notepad, has all three exclusion formats, and is not recaptured;
- two identical copies produce a diff reporting No differences, while images leave history unchanged;
- a copied
.batcontributes its full text, a copied.execontributes its filename plus(binary file), and a renamed binary is still treated as binary; - exactly two copied files—or two files copied with Explorer Copy as path—become the previous/current comparison pair in clipboard order, while copies of more than two files are ignored; missing, unreadable, empty, directory, oversized, and binary entries fall back to a filename with the reason appended;
- after one capture, right-clicking a second file offers Compare with current ClipDiff capture (including the current filename when file-backed), makes that file the new current entry, and immediately opens the selected diff viewer without changing the clipboard;
- selecting exactly two Explorer files offers Compare two selected files with ClipDiff without requiring a prior capture, makes the first Shell-supplied path previous and the second current, and immediately opens the selected viewer without changing the clipboard;
- selecting one file, three or more files, folders, or a mixture of files and folders hides the two-file command; invalid invocations do not alter history or cause file reads; clearing removes the individual-file command but leaves the two-file command available for exactly two files;
- pausing and quitting remove both Explorer commands, and Windows 11 exposes classic commands under Show more options when they are not in the primary menu;
- pausing skips copied text and resuming establishes a new baseline;
- clearing captured text does not modify the Windows clipboard;
- closing the diff window leaves the tray application running;
- occupying
Ctrl+Alt+Dleaves Show Diff usable from the menu; - a privacy-marked item is not read, and a recent unmarked value followed by a clear is removed;
- quitting removes the icon, unregisters native resources, and loses all captured content.
Run the same checks through Remote Desktop if Windows Server 2022 is the intended host.
ClipDiff cannot recover values copied before it starts because it does not persist clipboard content. If desired, create a shortcut to ClipDiff.exe in the folder opened by shell:startup.