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.

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.

Appendix: 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)