Determining a Printer’s Physical Margins

The .NET Framework does not provide a managed way to access a printer’s (device’s) physical margins. The Device class’ methods GetPhysicalMargins can be used to determine a printer’s physical margins. The printer may have a margin that is different from the PrintPreviewDialog’s margin. The methods below methods can be either called by supplying a handle to the device’s context or a Graphics object:

Imports System.Drawing.Printing

⋮

''' <summary>
'''   Provides information about a device.
''' </summary>
Public Class Device
    Private Declare Function GetDeviceCaps Lib "gdi32.dll" ( _
        ByVal hdc As IntPtr, _
        ByVal nIndex As Int32 _
    ) As Int32

    Private Const PHYSICALOFFSETX As Int32 = 112    ' In device units.
    Private Const PHYSICALOFFSETY As Int32 = 113    ' In device units.
    Private Const HORZRES As Int32 = 8      ' In pixels/dots.
    Private Const VERTRES As Int32 = 10     ' In pixels/dots.

    ''' <summary>
    '''   Gets a device's physical margins.
    ''' </summary>
    ''' <param name="Graphics">The device's <c>Graphics</c> object.</param>
    ''' <returns>
    '''   The device's physical margins in 0.001 inch units.
    ''' </returns>
    Public Shared Function GetPhysicalMargins( _
        ByVal Graphics As Graphics _
    ) As Margins
        Dim hDC As IntPtr = Graphics.GetHdc()
        Dim m As Margins = GetPhysicalMargins(hDC, Graphics)
        Graphics.ReleaseHdc(hDC)
        Return m
    End Function

    ''' <summary>
    '''   Gets a device's physical margins.
    ''' </summary>
    ''' <param name="hDC">Handle to the device context.</param>
    ''' <returns>
    '''   The device's physical margins in 0.001 inch units.
    ''' </returns>
    Public Shared Function GetPhysicalMargins( _
        ByVal hDC As IntPtr _
    ) As Margins
        Using g As Graphics = Graphics.FromHdc(hDC)
            Return GetPhysicalMargins(hDC, g)
        End Using
    End Function

    ''' <summary>
    '''   Gets a device's physical margins.
    ''' </summary>
    ''' <param name="hDC">Handle to the device context.</param>
    ''' <param name="Graphics">The device's <c>Graphics</c> object.</param>
    ''' <returns>
    '''   The device's physical margins in 0.001 inch units.
    ''' </returns>
    Private Shared Function GetPhysicalMargins( _
        ByVal hDC As IntPtr, _
        ByVal Graphics As Graphics _
    ) As Margins
        Dim m As New Margins()
        Dim ox As Int32 = GetDeviceCaps(hDC, PHYSICALOFFSETX)
        Dim oy As Int32 = GetDeviceCaps(hDC, PHYSICALOFFSETY)
        With m
            .Left = CInt(ox * 100 / Graphics.DpiX)
            .Top = CInt(oy * 100 / Graphics.DpiY)
            .Right = _
                CInt( _
                    (ox + GetDeviceCaps(hDC, HORZRES)) * 100 / _
                    Graphics.DpiX _
                )
            .Bottom = _
                CInt( _
                    (oy + GetDeviceCaps(hDC, VERTRES)) * 100 / _
                    Graphics.DpiY _
                )
        End With
        Return m
    End Function
End Class

Usage:

Dim p As New PrinterSettings()
For Each PrinterName As String In PrinterSettings.InstalledPrinters
    p.PrinterName = PrinterName
    Using g As Graphics = p.CreateMeasurementGraphics()
        MsgBox( _
            PrinterName & ControlChars.NewLine & _
            Device.GetPhysicalMargins(g).ToString() & " [0.01 inch]" _
        )
    End Using 
Next PrinterName