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

  1. Verify the DEVMODE came from the same printer/driver that the PTProvider was bound to.
  2. Check dmSize and dmDriverExtra are consistent with the buffer length passed in.
  3. Re-obtain a fresh DEVMODE via the queue's GetPrintCapabilities/DocumentProperties flow instead of caching.
  4. Inspect the HRESULT in the PrintQueueException for the native error code.
  5. 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

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


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)