{"record":{"id":"5040b90415992fcf","repo":"dotnet/wpf","slug":"sr-image-lockcountlimit-writeablebitmap","errorCode":null,"errorMessage":"SR.Image_LockCountLimit","messagePattern":"SR\\.Image_LockCountLimit","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Imaging/WriteableBitmap.cs","lineNumber":243,"sourceCode":"            WritePreamble();\n\n            TimeSpan timeoutSpan;\n            if (timeout == Duration.Automatic)\n            {\n                throw new ArgumentOutOfRangeException(nameof(timeout));\n            }\n            else if (timeout == Duration.Forever)\n            {\n                timeoutSpan = TimeSpan.FromMilliseconds(-1);\n            }\n            else\n            {\n                timeoutSpan = timeout.TimeSpan;\n            }\n\n            if (_lockCount == UInt32.MaxValue)\n            {\n                throw new InvalidOperationException(SR.Image_LockCountLimit);\n            }\n\n            if (_lockCount == 0)\n            {\n                // Try to acquire the back buffer by the supplied timeout, if the acquire call times out, return false.\n                if (!AcquireBackBuffer(timeoutSpan, true))\n                {\n                    return false;\n                }\n\n                Int32Rect rect = new Int32Rect(0, 0, _pixelWidth, _pixelHeight);\n\n                HRESULT.Check(UnsafeNativeMethods.WICBitmap.Lock(\n                    WicSourceHandle,\n                    ref rect,\n                    LockFlags.MIL_LOCK_WRITE,\n                    out _pBackBufferLock\n                    ));","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Imaging/WriteableBitmap.cs#L225-L261","documentation":"TryLock refuses to increment the lock count past UInt32.MaxValue. Every Lock/TryLock call must be balanced by Unlock; an unbalanced (or recursive re-entrant without unlock) locking pattern accumulates locks until the counter saturates, which is a programming bug, so the library throws InvalidOperationException with SR.Image_LockCountLimit.","triggerScenarios":"Calling TryLock/Lock repeatedly (e.g., in a render loop) without a matching Unlock, roughly 4 billion times, or leaking locks due to early returns in a try block that skips Unlock.","commonSituations":"Long-running WPF apps that lock WriteableBitmaps per frame and lose unlocks on exception paths; helpers that lock on the caller's behalf without unlocking.","solutions":["Balance every Lock/TryLock with exactly one Unlock, ideally in a finally block.","Wrap lock/unlock in IDisposable (using pattern) so exceptions cannot leak the lock.","Audit for re-entrant locking in per-frame code paths.","Reuse a single lock scope per frame instead of nested lock calls."],"exampleFix":"// before\nbitmap.Lock();\nWritePixels(...);\nif (error) return; // unlock lost\nbitmap.Unlock();\n// after\nbitmap.Lock();\ntry { WritePixels(...); }\nfinally { bitmap.Unlock(); }","handlingStrategy":"try-catch","validationCode":"if (bitmap._lockCount unchecked) — not public; instead track your own acquire count and assert it stays small: Debug.Assert(myLockDepth < 100);","typeGuard":null,"tryCatchPattern":"try { if (!bitmap.TryLock(Duration.Forever)) return; DoWork(); } finally { bitmap.Unlock(); }","preventionTips":["Always pair Lock/TryLock with Unlock in finally","Encapsulate in using/IDisposable wrapper","Avoid per-frame locking without release; audit hot loops"],"tags":["wpf","bitmap","lock-count","invalid-operation"],"backgroundTag":"invalid-state-transition","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}