dotnet/wpf · error · Win32Exception
throw new Win32Exception();
Error message
throw new Win32Exception();
What it means
D3DImage.SendPresent duplicates a shared-D3D-surface handle via the Win32 DuplicateHandle API so the rendered frame can be handed to the rendering thread. When DuplicateHandle fails (returns false), the code throws a bare Win32Exception, which carries the failing Win32 error code as its NativeErrorCode. This indicates the interop handshake that publishes the D3D surface to the composition thread could not be completed.
Solutions
- Check the thrown Win32Exception.NativeErrorCode and fix the underlying Win32 failure (often ERROR_INVALID_HANDLE from a disposed D3D device).
- Recreate the D3D device/surface and call SetBackBuffer again after a device-lost condition (check Device9 TestCooperativeLevel / device reset).
- Guard the render loop: skip Invalidate/_present if the D3D device or source handle is no longer valid, and reinitialize on failure.
- Reduce handle pressure (dispose D3DImage and device resources deterministically) if the failure is handle exhaustion.
Example fix
// before
_d3dImage.Invalidate(); // may throw Win32Exception if device lost
// after
if (_device.TestCooperativeLevel() == 0 /* D3D_OK */)
{
_d3dImage.Invalidate();
}
else
{
RecreateDeviceAndBackBuffer();
} Defensive patterns
Strategy: try-catch
Validate before calling
bool canPresent = _device != null && !_deviceLost && _backBuffer != IntPtr.Zero;
Type guard
static bool HasValidBackBuffer(D3DImage img) => img != null && img.IsFrontBufferAvailable;
Try / catch
try
{
_d3dImage.Invalidate();
}
catch (Win32Exception ex)
{
Log(ex.NativeErrorCode);
RecreateDeviceAndBackBuffer();
} Prevention
- Monitor IsFrontBufferAvailable and rebuild the back buffer when it flips to false/true.
- Dispose D3D resources deterministically before shutdown.
- Handle device-lost (TDR) conditions explicitly instead of letting present calls fail.
When it happens
Trigger: Calling D3DImage.SetBackBuffer/Invalidate when the shared handle duplication inside SendPresent fails — e.g. invalid or closed source handle, handle-table exhaustion, or the Direct3D device/surface having been destroyed while a present is in flight.
Common situations: GPU driver reset or device removal (TDR) mid-render; shutting down an application while D3DImage still presents; running out of GDI/handle resources under heavy load; mixing D3D9Ex shared surfaces across devices that cannot share resources.
Related errors
- Win32Exception(Marshal.GetLastWin32Error())
- new System.ComponentModel.Win32Exception(win32Error)
- SR.ChildWindowNotCreated
- SR.InvalidEventHandle
- The operation completed successfully.
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/47eda5469ee2cd34.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/D3DImage.cs:701
data.Type = MILCMD.MilCmdD3DImagePresent;
data.Handle = _duceResource.GetHandle(channel);
// We need to make sure the event stays alive in case we get collected before
// the composition thread processes the packet
IntPtr hDuplicate;
IntPtr hCurrentProc = MS.Win32.UnsafeNativeMethods.GetCurrentProcess();
if (!MS.Win32.UnsafeNativeMethods.DuplicateHandle(
hCurrentProc,
_canWriteEvent.SafeWaitHandle,
hCurrentProc,
out hDuplicate,
0,
false,
MS.Win32.UnsafeNativeMethods.DUPLICATE_SAME_ACCESS
))
{
throw new Win32Exception();
}
data.hEvent = (ulong)hDuplicate.ToPointer();
// Send packed command structure
// Note that the command is sent in its own batch (sendInSeparateBatch == true) 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 command would not be added to a separate batch, 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 D3DImage because Lock waits on _canWriteEvent 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.
channel.SendCommand(
(byte*)&data,
sizeof(DUCE.MILCMD_D3DIMAGE_PRESENT),
sendInSeparateBatch: true);View on GitHub (pinned to 81131a70a4)