dotnet/wpf · error · Win32Exception
Win32Exception (native Win32 error from DuplicateHandle…
Error message
Win32Exception (native Win32 error from DuplicateHandle failure)
What it means
In OnCommittingBatch, WriteableBitmap duplicates an event-handle via Win32 DuplicateHandle to signal copy-forward completion on the render thread. If DuplicateHandle fails (returns FALSE), the code throws Win32Exception carrying the native Win32 error code, meaning the handle duplication into the render process failed.
Solutions
- Check the Win32Exception.NativeErrorCode; if ERROR_NO_SYSTEM_RESOURCES/handle exhaustion, audit and fix handle leaks in the app (use Process Explorer to count handles).
- Ensure the WriteableBitmap and its host (e.g., Image) are not used after disposal/window close; re-create the bitmap if the previous one was torn down.
- Retry the operation; if transient under low-memory conditions, reduce simultaneous bitmap allocations.
- If it reproduces consistently under a sandbox/job object, grant the process rights to duplicate handles or run outside the restriction.
Defensive patterns
Strategy: try-catch
Validate before calling
// Check handle pressure before heavy bitmap work:
using var p = Process.GetCurrentProcess();
if (p.HandleCount > 10000) { /* fix leaks before creating more bitmap batches */ } Try / catch
try { bitmap.WritePixels(rect, buffer, stride, 0); }
catch (Win32Exception ex) { Log(ex.NativeErrorCode); if (IsHandleExhaustion(ex.NativeErrorCode)) ReduceHandleUsage(); else throw; } Prevention
- Audit and dispose GDI/kernel handles; monitor HandleCount in production
- Keep WriteableBitmap instances alive while in use; avoid use-after-dispose
- Test the app under job-object/sandbox restrictions if you deploy in AppContainer environments
When it happens
Trigger: Committing a WriteableBitmap render batch when DuplicateHandle fails — typically due to handle exhaustion, insufficient access rights on the source handle, or an invalid/closed source process handle.
Common situations: Systems with handle leaks/exhaustion (thousands of GDI/kernel handles), running under restricted jobs or AppContainer sandboxes that limit handle duplication, or heavy WriteableBitmap churn causing race conditions on teardown.
Related errors
- SR.InvalidEventHandle
- ArgumentNullException
- D3DERR_OUTOFVIDEOMEMORY
- E_OUTOFMEMORY
- InvalidOperationException
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/ef385bb76694668e.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Imaging/WriteableBitmap.cs:1303
// We are going to pass an event in the command packet we send to
// the composition thread. We need to make sure the event stays
// alive in case we get collected before the composition thread
// processes the packet. We do this by duplicating the event
// handle, and the composition thread will close the handle after
// signalling it.
IntPtr hDuplicate;
IntPtr hCurrentProc = MS.Win32.UnsafeNativeMethods.GetCurrentProcess();
if (!MS.Win32.UnsafeNativeMethods.DuplicateHandle(
hCurrentProc,
_copyCompletedEvent.SafeWaitHandle,
hCurrentProc,
out hDuplicate,
0,
false,
MS.Win32.UnsafeNativeMethods.DUPLICATE_SAME_ACCESS
))
{
throw new Win32Exception();
}
DUCE.MILCMD_DOUBLEBUFFEREDBITMAP_COPYFORWARD command;
command.Type = MILCMD.MilCmdDoubleBufferedBitmapCopyForward;
command.Handle = _duceResource.GetHandle(channel);
command.CopyCompletedEvent = (UInt64) hDuplicate.ToInt64();
// Note that the batch is closed after the sendcommand because this method is called under the
// context of the MediaContext.CommitChannel and the command needs to make it into the current set of changes which are
// being commited to the compositor. If the batch is not closed, it would go into the
// "future" batch which would not get submitted this time around. This leads to a dead-lock situation which occurs when
// the app calls Lock on the WriteableBitmap because Lock waits on _copyCompletedEvent which the compositor sets when it sees the
// Present command. However, since the compositor does not get the Present command, it will not set the event and the
// UI thread will wait forever on the compositor which will cause the application to stop responding.
// Another option is to send the command in its own batch (instead of closing the batch). This doesn't work in all cases
// because the command for creating the resource handle (AddRefOnChannelCore) or the command for initializing the resource (UpdateBitmapSourceResource)
// could be in the "future" batch thus crashing the CopyForward operation in this batch.
View on GitHub (pinned to 81131a70a4)