• File: vs-code-portable.md.php
  • Full Path: /home/daemondevin/public_html/blog/content/posts/vs-code-portable.md.php
  • Date Modified: 10/19/2026 2:56 PM
  • File size: 10.58 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?php http_response_code(404); exit; ?>
---
title: VS Code Portable
date: 2026-10-19 07:34
updated: 2026-10-19 07:56
status: published
type: post
tags: [VS Code, portable, pac-man, paf]
image: /~daemondevin/blog/uploads/2026/10/vscode-e7a6e2.jpg
---

My VS Code Portable now runs on the newest pac-man, and that means a portable launcher that cleans up after itself even when it crashes. Every change it makes to Windows is written down before it happens and undone when VS Code closes, or on the next launch if something went wrong.

If you haven't met it before: [pac-man](https://github.com/daemondevin/pac-man) (PortableApps Compiler & Management) is my launcher framework for portable Windows apps. It's built on top of the PortableApps.com Launcher (PAL), but it isn't PAL and doesn't replace it. PAL deliberately stays away from apps that need services, drivers, firewall rules, scheduled tasks or shell integration. pac-man exists for exactly those apps, and treats every launch like a transaction rather than an install.

This post covers what changed in the framework, what that made possible for VS Code Portable, and the bugs that fell out along the way.

## The journal: every change comes with its undo

The core of this release is a journal: before the launcher changes anything on your PC, it writes down how to undo that change. When the app closes, the launcher works back through the list, newest first, until nothing is left.

If the launcher is killed, the PC loses power, or the USB stick gets pulled mid-session, the undo steps are still in the journal. The next launch notices, tells you, and rolls back the old session before changing anything new.

A few details that make this hold up in practice:

- **It lives on the PC, not the stick.** The journal is kept under `HKCU\Software\pac-man\Journal`, and backup files under `%LOCALAPPDATA%\pac-man\Journal`, so recovery works even if the stick comes back with a different drive letter.
- **Undo steps are safe to run twice.** If a recovery is itself interrupted, the next one simply picks up where it stopped.
- **One rollback, in a fixed order.** Segments are undone in the reverse of the order they were applied, so nothing is removed while something else still depends on it.
- **It covers 14 kinds of change.** That's registry keys and values, files and folders, links, fonts, services, COM DLLs and type libraries, scheduled tasks, drivers, text lines and commands.

## Five segments rewritten on the journal

Five segments were rebuilt from scratch on top of the journal this round. Each one is switched on with a single line under `[Activate]` and configured with numbered sections in the launcher ini.

| Segment | What it does | How it's undone |
| --- | --- | --- |
| Associations | File types, URL protocols and context menu entries, written under `HKCU\Software\Classes` only. It never touches Windows' own default-app choice; a type that already has a handler just gains an "Open with" entry. | New keys are deleted, replaced keys are re-imported from a backup, changed values are put back. |
| Firewall | Temporary firewall rules through netsh, tagged so the launcher only ever removes its own. | Its rules are deleted; a rule someone else made is left alone. Commands that need admin rights wait until they have them. |
| Tasks | Scheduled tasks the app needs while it runs. Tasks that would store a password are refused. | The task is deleted, and a task it replaced is re-created from its exported XML. |
| Drivers | Driver packages added to the driver store for the session. | The package is found by content in the driver store and removed with pnputil. |
| Runtime | Visual C++ and .NET checks that run before anything else changes. A missing runtime can come from a portable copy (put on PATH, or set as DOTNET\_ROOT) for the app's process only. | Nothing to undo. The one exception, installing a runtime system-wide, only happens after a prompt that says it's permanent. |

Fonts, Symlinks, RegDLL, Services and Hosts already run on the journal, so a launcher can now combine any of these and still get one clean rollback, in a fixed order, on exit or after a crash.

## What VS Code Portable gets out of it

VS Code Portable is the first package rebuilt on all of this, and about 150 lines of hand-written registry code are gone from it. Here's what you'll notice:

- **Open with Code (Portable).** While VS Code runs, `.txt`, `.ini`, `.xml`, `.js` and `.css` files get an "Open with" entry, and files, folders and folder backgrounds get an "Open with Code (Portable)" context menu. Windows keeps its own defaults, and everything is gone again when VS Code closes.
- **Files open in the running window.** Opening a file from Explorer, the context menu or SendTo while VS Code is already open now hands it to the running instance instead of doing nothing.
- **Your installed VS Code is left alone.** VS Code registers the `vscode://` protocol for itself every time it starts. The launcher now journals that key first, so if you also have VS Code installed, its own registration comes back exactly as it was.
- **No admin prompt.** The launcher no longer asks to run elevated. That matters because of the next point.
- **Your Git, Python and Node.js actually reach VS Code.** Point the user config at portable copies of them and they go in front of `PATH`, along with `HOME`, `PYTHONUSERBASE` and the npm prefix if you ask for them. Before, the launcher started VS Code through Explorer, which quietly threw that environment away.
- **Extensions stay put.** Extensions now live in `Data\code\extensions` and survive between sessions.
- **Bundled VSIX files are offered once each.** Drop a `.vsix` in `App\DefaultData\code\extensions` and it's offered on the next launch. Your answer is remembered, so a file added later still gets offered.
- **Windows 10 or later, and no .NET.** VS Code doesn't use .NET, so the old .NET 4.5 requirement is gone.

## Updates that can't leave you with a broken VS Code

The built-in updater now treats an update as all-or-nothing: you either end up with the new version or keep the old one, never half of each. With `UpdateCheck=true`, the launcher asks Microsoft's update service for the latest version, and if it's newer it asks you before downloading.

The swap works like this:

1. The current `App\VSCode` folder is moved aside as a backup, and that move goes straight into the journal.
2. The new version is extracted into a fresh `App\VSCode`.
3. Only once `Code.exe` is there is the update committed and the backup deleted.

If the download fails, the archive is bad, or the launcher is killed halfway through extracting, the journal deletes the half-finished folder and moves the old version back. A crash even gets caught before the launcher checks for `Code.exe`, so it never stops with "Code.exe could not be found" when the old copy is sitting right there.

The download also shows its progress now. The banner grows a progress bar with a running count, like `45%  (60.2 / 133.4 MB)`. If the server doesn't say how big the file is, you get a moving bar and a megabyte counter instead.

## A smarter installer

The installer now tells my packages apart from other people's, and won't reinstall a version you already have without asking.

**Installing over someone else's VS Code Portable.** If the copy you're installing over was made by a different developer, you get a prompt that names them before anything is overwritten. The check now also catches packages that don't list a developer, using their publisher instead, so they no longer slip through silently. My own older packages are still recognized. Before this fix, the check sometimes flagged my own installs as "another developer", and clicking OK cleared out your settings.

**Installing the version you already have.** The installer reads the version straight out of the VS Code archive it just downloaded and compares it with the `Code.exe` you have. Then:

- Newer version: installs as normal.
- Same version, or yours is newer (the launcher's own updater may have got there first): asks whether to install anyway, with No as the default.
- Silent install: stops quietly instead of asking.
- Either version can't be read: installs anyway, so the check can never block an install.

## Bugs squashed along the way

Rebuilding VS Code Portable turned up a handful of long-standing bugs. Some had been quietly breaking features for a while.

| Where | What went wrong | Now |
| --- | --- | --- |
| VS Code Portable | Extensions were installed to your user profile and deleted on exit, because the launcher passed a VS Code flag that doesn't exist. | Extensions live in `Data\code\extensions` and stay. |
| VS Code Portable | After moving to another drive, path fixes were applied to the wrong copies of `settings.json` and `storage.json`. | The right files are fixed, plus `extensions.json`. |
| VS Code Portable | The old context menu commands pointed at a launcher path variable that was never set. | The path is set, and the menu goes through the launcher. |
| VS Code Portable | Opening a file while VS Code was running did nothing; the second launch just quit. | It opens in the running window. |
| VS Code Portable | Bundled VSIX files were never installed. | Each one is offered once. |
| VS Code Portable | Several translations had turned into `????` and garbled characters. | Rewritten and saved as proper UTF-8. |
| pac-man | Setting a `[Dependencies]` option to `false` still switched it on, and two options never switched on at all. | `false` means off, and every option is wired up. |
| pac-man | Two overlapping launches could run the runtime checks twice. | They run once, in the first launch. |
| pac-man | Builds threw 168 missing-language-string warnings. | Zero. |

## Getting it, and what's next

VS Code Portable is on the [releases page](https://github.com/daemondevin/VSCodePortable/releases/latest), and pac-man is on [GitHub](https://github.com/daemondevin/pac-man) if you want to build your own launchers on it.

If you're upgrading, one setting was renamed. In `VSCodePortable.ini` next to the launcher, change `Associations=` to `FileAssociations=`. A fresh copy of the file is in `Other\Source` if you'd rather start clean.

Everything above has been through crash and recovery testing, with every change checked against a snapshot of the system from before the launch. Next up:

- Need testers feedback
- Clearing out code the framework no longer uses.
- Bringing the documentation pages up to date with the new segments, including a guide to writing your own.

If something in this release breaks for you, [open an issue](https://github.com/daemondevin/pac-man/issues) and include an export of the HKCU\\Software\\pac-man\\Journal key along with the folder under `%LOCALAPPDATA%\pac-man\Journal`.