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 = 0

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Check Marshal.GetLastWin32Error() to identify the specific Win32 failure.
  2. Ensure the clipboard copy path is not entered concurrently from multiple threads (the _playLock-style discipline).
  3. Retry the alloc/lock sequence once; if it persists, treat as a fatal environment problem and report it.
  4. 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

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


AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13). Data as JSON: /api/errors/d01668296d129785. Report an issue: GitHub.