{"record":{"id":"a595f0ca893d7d0f","repo":"dotnet/wpf","slug":"sr-image-mustbelocked","errorCode":null,"errorMessage":"SR.Image_MustBeLocked","messagePattern":"SR\\.Image_MustBeLocked","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/D3DImage.cs","lineNumber":129,"sourceCode":"        ///\n        ///     For best performance by type:\n        ///         IDirect3DSurface9\n        ///             Vista WDDM: non-lockable, created on IDirect3DDevice9Ex with \n        ///                         D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES and\n        ///                         D3DCAPS2_CANSHARERESOURCE support.\n        ///             Vista XDDM: Doesn't matter. Software copying is fastest.\n        ///             non-Vista:  Lockable with GetDC support for the pixel format and \n        ///                         D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES support.\n        ///\n        /// </summary>\n        public void SetBackBuffer(D3DResourceType backBufferType, IntPtr backBuffer, bool enableSoftwareFallback)\n        {\n\n            WritePreamble();\n            \n            if (_lockCount == 0)\n            {\n                throw new InvalidOperationException(SR.Image_MustBeLocked);\n            }\n\n            // In case the user passed in something like \"(D3DResourceType)-1\"\n            if (backBufferType != D3DResourceType.IDirect3DSurface9)\n            {\n                throw new ArgumentOutOfRangeException(nameof(backBufferType));\n            }\n\n            // Early-out if the current back buffer equals the new one. If the front buffer\n            // is not available and software fallback is not enabled, _pUserSurfaceUnsafe \n            // will be null and this check will fail. We don't want a null backBuffer to \n            // early-out when the front buffer isn't available.\n            if (backBuffer != IntPtr.Zero && backBuffer == _pUserSurfaceUnsafe)\n            {\n                return;\n            }\n            \n            SafeMILHandle newBitmap = null;","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/D3DImage.cs#L111-L147","documentation":"D3DImage.SetBackBuffer requires the image to be in a locked state: a lock must have been acquired via Lock/TryLock and not yet released with Unlock. If _lockCount is 0, calling SetBackBuffer throws InvalidOperationException(Image_MustBeLocked). The lock ensures the CPU and the composition thread agree on when the back-buffer pointer can be swapped safely.","triggerScenarios":"Calling SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface) without a preceding Lock() or successful TryLock(); calling it after Unlock already released the last lock; calling it from a different thread than the one that took the lock.","commonSituations":"D3D/Direct3D9 interop render loops where the initial setup path assigns the back buffer before entering the lock/render/unlock cycle; races where another thread unlocked between checking and calling; forgetting the lock in a one-time back-buffer assignment.","solutions":["Acquire the lock first: call Lock() (or TryLock with a timeout and check the bool result) before SetBackBuffer, then Unlock when done.","Keep every SetBackBuffer/AddDirtyRect call inside the same Lock/Unlock bracket.","Ensure the Lock, SetBackBuffer, AddDirtyRect, Unlock sequence runs on one thread (the D3DImage's UI thread affinity)."],"exampleFix":"// before\nd3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface); // throws: not locked\n\n// after\nd3dImage.Lock();\ntry\n{\n    d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface);\n}\nfinally\n{\n    d3dImage.Unlock();\n}","handlingStrategy":"validation","validationCode":"if (!isLocked) { d3dImage.Lock(); isLocked = true; }\nd3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surfacePtr);","typeGuard":null,"tryCatchPattern":"try { d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, ptr); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"lock\"))\n{\n    d3dImage.Lock();\n    d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, ptr);\n}","preventionTips":["Always wrap SetBackBuffer inside Lock/try/finally-Unlock.","Keep lock state on one thread (the UI thread).","Assign the back buffer once during setup, under lock."],"tags":["wpf","d3dimage","interop","invalid-operation","lock-state"],"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"}