SubtitleEdit/subtitleedit · error · InvalidOperationException
Failed to lock global memory
Error message
Failed to lock global memory
What it means
Thrown in CopyImageToClipboardWindows right after a successful GlobalAlloc: GlobalLock(hGlobal) returned IntPtr.Zero even though the handle was allocated. The code frees the handle before throwing. A zero from GlobalLock is rare and usually indicates the block is invalid/discardable or a low-level memory-manager corruption; in practice it is far less common than the GlobalAlloc failure.
Source
Thrown at src/ui/Logic/ClipboardHelper.cs:220
// Calculate sizes
var stride = width * 4; // 4 bytes per pixel (BGRA)
var imageSize = stride * height;
var headerSize = Marshal.SizeOf<BITMAPINFOHEADER>();
var totalSize = headerSize + imageSize;
// Allocate global memory
hGlobal = GlobalAlloc(GMEM_MOVEABLE, new UIntPtr((uint)totalSize));
if (hGlobal == IntPtr.Zero)
{
throw new InvalidOperationException("Failed to allocate global memory");
}
IntPtr ptr = GlobalLock(hGlobal);
if (ptr == IntPtr.Zero)
{
GlobalFree(hGlobal);
throw new InvalidOperationException("Failed to lock global memory");
}
try
{
// Write BITMAPINFOHEADER
var header = new BITMAPINFOHEADER
{
biSize = (uint)headerSize,
biWidth = width,
biHeight = height, // positive for bottom-up DIB
biPlanes = 1,
biBitCount = 32,
biCompression = BI_RGB,
biSizeImage = (uint)imageSize,
biXPelsPerMeter = 0,
biYPelsPerMeter = 0,
biClrUsed = 0,
biClrImportant = 0View on GitHub (pinned to 17a9f07487)
Solutions
- Check Marshal.GetLastWin32Error() to identify the specific Win32 failure.
- Ensure the clipboard copy path is not entered concurrently from multiple threads (the _playLock-style discipline).
- Retry the alloc/lock sequence once; if it persists, treat as a fatal environment problem and report it.
- Investigate native dependencies for heap corruption if the error recurs reliably.
Example fix
// before
IntPtr ptr = GlobalLock(hGlobal);
if (ptr == IntPtr.Zero) { GlobalFree(hGlobal); throw new InvalidOperationException("Failed to lock global memory"); }
// after - one retry and a richer error
IntPtr ptr = IntPtr.Zero;
for (int i = 0; i < 2 && ptr == IntPtr.Zero; i++) ptr = GlobalLock(hGlobal);
if (ptr == IntPtr.Zero)
{
var winErr = Marshal.GetLastWin32Error();
GlobalFree(hGlobal);
throw new InvalidOperationException($"Failed to lock global memory (Win32 error {winErr}).");
} Defensive patterns
Strategy: try-catch
Try / catch
IntPtr ptr = IntPtr.Zero;
for (int i = 0; i < 2 && ptr == IntPtr.Zero; i++) ptr = GlobalLock(hGlobal);
if (ptr == IntPtr.Zero)
{
var e = Marshal.GetLastWin32Error();
GlobalFree(hGlobal);
throw new InvalidOperationException($"Failed to lock global memory (Win32 {e}).");
} Prevention
- Do not enter the clipboard copy path concurrently from multiple threads.
- Retry the lock once before failing.
- Investigate native dependencies if GlobalLock reliably fails on a fresh block.
When it happens
Trigger: GlobalLock fails on a freshly allocated moveable block: the handle was invalidated between alloc and lock (e.g. by another thread freeing it), memory-manager corruption, or a discarded/discardable block under memory pressure.
Common situations: Concurrent clipboard operations racing on the same handle; heap corruption from a native dependency; extreme memory pressure causing the block to be discarded; AV/hook interfering with the global heap.
Related errors
- Failed to allocate global memory
- Failed to open clipboard
- Failed to empty clipboard
- Failed to set clipboard data
- Failed to decode bitmap
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/d01668296d129785.
Report an issue: GitHub.