Windows Process Snapshotting (PSS): Capture, Walk, and Release
Use Windows Process Snapshotting APIs with explicit capture flags, a walk marker, correct end-of-walk handling, and deterministic HPSS cleanup.
When a Windows diagnostic needs a bounded view of a process, a live query may not provide the data the collector needs. Process Snapshotting (PSS) gives a Win32 application an explicit capture API and a handle through which it can query or walk selected snapshot information. The API is useful only when the capture flags, walk class, access rights, and cleanup lifetime are treated as one contract.
PSS is not a request for “everything about the process.” PssCaptureSnapshot accepts flags that select what to capture. A thread-only capture, a handle-table capture, a virtual-address-space map, and a virtual-address clone are different products. In particular, PSS_CAPTURE_VA_SPACE captures region metadata but not the contents of those pages; PSS_CAPTURE_VA_CLONE requests cloneable page contents. Do not describe the former as a memory dump.
Some capture flags have prerequisites. For example, PSS_CAPTURE_VA_SPACE_SECTION_INFORMATION is valid only with PSS_CAPTURE_VA_SPACE and requires PROCESS_VM_READ on the target-process handle. Validate flag combinations and access rights together; a flag name alone does not tell you whether the requested capture can be produced.
Choose the data before capturing
Start from the diagnostic question. If the report needs thread identities and walkable thread entries, request thread data. Add thread contexts only when register state is actually needed. If it needs handle names, request handle capture and the relevant name-information flag; PSS_CAPTURE_HANDLES alone captures handle values, not every descriptive field.
This distinction avoids both missing data and accidental over-capture. A later call to PssWalkSnapshot can enumerate only information represented in the snapshot. If the capture flags did not include the required class, a walk failure is not evidence that the target had no such objects.
PSS is documented for Windows 8.1 desktop applications and Windows Server 2012 R2. Check the target OS and the Windows SDK headers used to build the collector; do not infer API availability from a newer development machine.
Walk with a marker and recognize completion
The capture returns an HPSS snapshot handle. A walk marker (HPSSWALK) holds a walk position. PssWalkSnapshot advances the marker during a call; use its explicit return status rather than inferring completion from a missing entry. ERROR_NO_MORE_ITEMS is the normal end of a completed walk; it is not a successful entry and should not be logged as a collection failure. ERROR_NOT_FOUND, by contrast, means the requested information was not present in the snapshot.
The following lifecycle sketch captures and walks thread information. The target process handle is assumed to have been opened intentionally with rights appropriate to the selected capture flags; error formatting, reporting, and the collector’s process-handle ownership are application-specific.
HPSS snapshot = nullptr;
HPSSWALK marker = nullptr;
DWORD status = PssCaptureSnapshot(
targetProcess,
PSS_CAPTURE_THREADS,
0,
&snapshot);
if (status != ERROR_SUCCESS) {
ReportWin32Error(status);
return;
}
status = PssWalkMarkerCreate(nullptr, &marker);
if (status == ERROR_SUCCESS) {
for (;;) {
PSS_THREAD_ENTRY entry{};
status = PssWalkSnapshot(
snapshot,
PSS_WALK_THREADS,
marker,
&entry,
sizeof(entry));
if (status == ERROR_NO_MORE_ITEMS) {
break;
}
if (status != ERROR_SUCCESS) {
ReportWin32Error(status);
break;
}
ConsumeThreadEntry(entry);
}
} else {
ReportWin32Error(status);
}
if (marker != nullptr) {
DWORD markerStatus = PssWalkMarkerFree(marker);
if (markerStatus != ERROR_SUCCESS) {
ReportWin32Error(markerStatus);
}
}
DWORD freeStatus = PssFreeSnapshot(GetCurrentProcess(), snapshot);
if (freeStatus != ERROR_SUCCESS) {
ReportWin32Error(freeStatus);
}
This sketch requests thread entries without thread contexts. If a collector also requests context, Microsoft’s PSS_THREAD_ENTRY documentation says its ContextRecord pointer is valid for the walk-marker lifetime. Copy context data needed after freeing the marker into application-owned storage. Check both marker creation and snapshot cleanup, even when enumeration failed.
Treat handles and flags as an owned lifecycle
The snapshot handle remains allocated until PssFreeSnapshot succeeds. Its process parameter identifies the process that contains the snapshot, not necessarily the process being inspected. For the local snapshot in this example, pass GetCurrentProcess(); Microsoft documents PROCESS_VM_READ, PROCESS_VM_OPERATION, and PROCESS_DUP_HANDLE as the required access rights on the process handle. Remote cleanup has additional constraints: a remote free requires a snapshot created with PSS_CREATE_USE_VM_ALLOCATIONS or one duplicated remotely. This sketch handles only a local snapshot. Keep the target handle valid for capture and any operations that require it, then close it according to the collector’s normal ownership policy.
The walk marker is a separate resource. Create one for a walk, free it after use, and do not share one marker between concurrent walks unless the caller deliberately coordinates its position. A marker is mutable state: every successful walk advances it, and rewinding or seeking it changes where later calls resume.
Do not combine flags just because they are available. Handle names, type-specific details, trace information, thread contexts, and virtual-address clones serve different investigations and may add cost or sensitive data. Record the flags actually requested so the output can be interpreted later.
Validate the collector on a disposable target
Build a small test process with a known number of worker threads and handles. Capture only threads, walk until ERROR_NO_MORE_ITEMS, and confirm that the report identifies the requested class and records the exact flags supplied to PssCaptureSnapshot. Then test a deliberately incompatible walk class and verify that the program distinguishes missing snapshot data from normal completion.
Exercise cleanup after every failure point: capture failure, walk-marker creation failure, an error during enumeration, and a successful full walk. Close the target handle only after all operations that require it are complete. In production, emit the API name, numeric Win32 status, target identity, requested flags, operating-system version, and whether PssFreeSnapshot succeeded, but do not write sensitive process contents to ordinary logs.
PSS supplies a structured snapshot facility, not an automatic diagnosis or a universally complete dump. Its value comes from asking a narrow question, capturing the matching data, walking the requested classes correctly, and releasing both the marker and snapshot on every path.
Related:
- Windows Wait Chain Traversal: Diagnosing Hangs Without Guessing
- Windows WER LocalDumps: Capturing User-Mode Crashes for Diagnosis
Sources:
- Microsoft Learn: PssCaptureSnapshot function - capture parameters, snapshot handle, return status, and supported Windows versions.
- Microsoft Learn: PSS_CAPTURE_FLAGS enumeration - flag-specific capture scope, including thread, handle, VA-space, and VA-clone behavior.
- Microsoft Learn: PSS_WALK_INFORMATION_CLASS enumeration - classes that can be enumerated from captured snapshot data.
- Microsoft Learn: PSS_THREAD_ENTRY structure - thread walk output fields and context-record lifetime.
- Microsoft Learn: PssWalkSnapshot function - walk-marker advancement and normal/error return codes.
- Microsoft Learn: PssWalkMarkerCreate function - creation and allocator behavior for a walk marker.
- Microsoft Learn: PssWalkMarkerFree function - marker cleanup and return status.
- Microsoft Learn: PssFreeSnapshot function - snapshot cleanup, process-handle access requirements, and ownership remarks.