dotnet/wpf · error · PrintQueueException
PrintConfig.Provider.DevMode2PTFail
PrintConfig.Provider.DevMode2PTFail
Error message
PrintConfig.Provider.DevMode2PTFail
What it means
PTProvider.ConvertDevModeToPrintTicket checks the HRESULT of the native PTConvertDevModeToPrintTicket call; on failure it throws PrintQueueException with reason PrintConfig.Provider.DevMode2PTFail. This means the driver provider could not convert the supplied binary DEVMODE structure into a PrintTicket document.
Solutions
- Verify the DEVMODE came from the same printer/driver that the PTProvider was bound to.
- Check dmSize and dmDriverExtra are consistent with the buffer length passed in.
- Re-obtain a fresh DEVMODE via the queue's GetPrintCapabilities/DocumentProperties flow instead of caching.
- Inspect the HRESULT in the PrintQueueException for the native error code.
- If the DEVMODE is from a different driver, convert via PrintQueue interpolation rather than direct conversion.
Example fix
// before byte[] devMode = cachedDevModeFromOtherDriver; // DEVMODE from a different driver var pt = provider.ConvertDevModeToPrintTicket(devMode); // after byte[] devMode = currentQueue.GetPrintCapabilities(); // or fresh MARSHAL from current queue var pt = provider.ConvertDevModeToPrintTicket(freshDevModeFromSameDriver);
Defensive patterns
Strategy: validation
Validate before calling
// devMode bytes must be freshly obtained for the SAME queue the provider is bound to
var devMode = queue.CurrentJobSettings == null ? null : GetFreshDevMode(queue); // via Marshal/DocumentProperties
if (devMode == null || devMode.Length == 0)
throw new InvalidOperationException("No valid DEVMODE for this queue."); Type guard
static bool IsPlausibleDevMode(byte[] buf)
{
if (buf == null || buf.Length < 40) return false;
short dmSize = BitConverter.ToInt16(buf, 0);
return dmSize >= 40 && dmSize <= buf.Length;
} Try / catch
try { pt = provider.ConvertDevModeToPrintTicket(devMode); }
catch (PrintQueueException ex) when (ex.Message.Contains("PrintConfig.Provider.DevMode2PTFail"))
{
pt = queue.DefaultPrintTicket; // fall back to defaults
} Prevention
- Never reuse DEVMODE buffers across different printers or driver versions.
- Check dmSize/dmDriverExtra consistency with the byte buffer length.
- Re-fetch DEVMODE from the current queue instead of caching long-lived copies.
When it happens
Trigger: Calling PTProvider.ConvertDevModeToPrintTicket(devMode byte[]) where the native conversion fails: DEVMODE buffer size mismatch, DEVMODE from a different driver, or corrupt/partial DEVMODE bytes.
Common situations: Caching a DEVMODE from one printer and applying it to another; DEVMODE struct marshaled with wrong dmSize/dmDriverExtra; DEVMODE obtained from an old driver version; passing a truncated byte buffer.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- PrintConfig.Provider.PT2DevModeFail
- PrintConfig.Provider.BindFail
- PrintConfig.Provider.GetPrintCapFail
- PrintConfig.Provider.MergeValidateFail
- ArgumentException.NonNegativeValue (Parameter 'squareScale')
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/dde614d57952380c.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/ReachFramework/PrintConfig/PTProvider.cs:411
// Allocate unmanaged buffer and copy devMode byte array into the unmanaged buffer
Marshal.Copy(devMode, 0, umDevMode, devMode.Length);
IStream printTicketStream = CreateStreamOnHGlobal();
try
{
uint hResult = UnsafeNativeMethods.PTConvertDevModeToPrintTicket(_providerHandle,
(uint)devMode.Length,
new HandleRef(this, umDevMode),
(uint)scope,
printTicketStream);
if (PTUtility.IsSuccessCode(hResult))
{
RewindIStream(printTicketStream);
return MemoryStreamFromIStream(printTicketStream);
}
throw new PrintQueueException((int)hResult,
"PrintConfig.Provider.DevMode2PTFail",
_deviceName);
}
finally
{
DeleteIStream(ref printTicketStream);
}
}
finally
{
if (umDevMode != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(umDevMode);
umDevMode = IntPtr.Zero;
}
}
}
View on GitHub (pinned to 81131a70a4)