views:

264

answers:

1

This is the same as this question, with one remark: We manage to get the printerSettings modified, but how can we save them back as the default printer settings? (the original question does not post/answer this)

The code I'm using right now:

<DllImport("winspool.Drv", EntryPoint:="DocumentPropertiesW", SetLastError:=True, _
    ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
  Shared Function DocumentProperties(ByVal hwnd As IntPtr, ByVal hPrinter As IntPtr, _
      <MarshalAs(UnmanagedType.LPWStr)> ByVal pDeviceName As String, _
      ByVal pDevModeOutput As IntPtr, ByVal pDevModeInput As IntPtr, ByVal fMode As Integer) As Integer

   End Function

   <DllImport("kernel32.dll")> _
   Shared Function GlobalLock(ByVal hMem As IntPtr) As IntPtr

   End Function

   <DllImport("kernel32.dll")> _
   Shared Function GlobalUnlock(ByVal hMem As IntPtr) As Boolean

   End Function
   <DllImport("kernel32.dll")> _
   Shared Function GlobalFree(ByVal hMem As IntPtr) As Boolean

   End Function

   <DllImport("winspool.drv")> _
   Shared Function OpenPrinter(ByVal pPrinterName As String, ByRef hPrinter As IntPtr, ByVal pDefault As IntPtr) As Integer

   End Function

   <DllImport("winspool.drv")> _
   Private Shared Function ClosePrinter(ByVal phPrinter As IntPtr) As Integer

   End Function

(for DocumentProperties I've tried also making pDevModeInput a ByRef parameter)

And inside OpenPrinterPropertiesDialog I have tried:

Variant 1:

Public Sub OpenPrinterPropertiesDialog(ByVal iPrinterSettings As PrinterSettings)
      Dim hDevMode As IntPtr = iPrinterSettings.GetHdevmode()
      Dim pDevMode As IntPtr = GlobalLock(hDevMode)
      Dim sizeNeeded As Integer = DocumentProperties(mHandle, IntPtr.Zero, iPrinterSettings.PrinterName, pDevMode, pDevMode, 0)
      Dim devModeData As IntPtr = Marshal.AllocHGlobal(sizeNeeded)
      Dim fMode As Integer = 14
      Dim returnCode As Integer = DocumentProperties(mHandle, IntPtr.Zero, iPrinterSettings.PrinterName, devModeData, pDevMode, fMode)

      GlobalUnlock(hDevMode)
      iPrinterSettings.SetHdevmode(devModeData)
      iPrinterSettings.DefaultPageSettings.SetHdevmode(devModeData)

      GlobalFree(hDevMode)
      Marshal.FreeHGlobal(devModeData)
   End Sub

Variant 2:

Public Sub OpenPrinterPropertiesDialog(ByVal iPrinterSettings As PrinterSettings)
      Dim printerName As String = iPrinterSettings.PrinterName
      Dim handle As IntPtr
      OpenPrinter(printerName, handle, IntPtr.Zero)

      Dim hDevMode As IntPtr = iPrinterSettings.GetHdevmode(iPrinterSettings.DefaultPageSettings)
      Dim pDevMode As IntPtr = GlobalLock(hDevMode)
      Dim sizeNeeded As Integer = DocumentProperties(mHandle, IntPtr.Zero, iPrinterSettings.PrinterName, pDevMode, pDevMode, 0)
      Dim devModeData As IntPtr = Marshal.AllocHGlobal(sizeNeeded)
      Dim fMode As Integer = 14
      Dim returnCode As Integer = DocumentProperties(mHandle, IntPtr.Zero, iPrinterSettings.PrinterName, devModeData, pDevMode, fMode)

      GlobalUnlock(hDevMode)
      iPrinterSettings.SetHdevmode(devModeData)
      iPrinterSettings.DefaultPageSettings.SetHdevmode(devModeData)
      returnCode = DocumentProperties(IntPtr.Zero, IntPtr.Zero, iPrinterSettings.PrinterName, devModeData, pDevMode, 2)

      ClosePrinter(handle)
      GlobalFree(hDevMode)
      Marshal.FreeHGlobal(devModeData)
   End Sub

Any ideas of why the changes are not saved back?

A: 

To get it working I had to change the code based on the code found here (thanks umeshb for sharing it!)

The code I'm using right now (working on XP) is: (feel free to optimize it)

Public Class PrinterSettingsHelper
#Region "Private Variables"
   Private gPrinter As IntPtr = New System.IntPtr()
   Private gPrinterValues As PRINTER_DEFAULTS = New PRINTER_DEFAULTS()
   Private gPInfo As PRINTER_INFO_2 = New PRINTER_INFO_2()
   Private gDevMode As DEVMODE
   Private gPtrDevMode As IntPtr
   Private gPtrPrinterInfo As IntPtr
   Private gSizeOfDevMode As Integer = 0
   Private gLastError As Integer
   Private gNBytesNeeded As Integer
   Private gNRet As Long
   Private gIntError As Integer
   Private gNTemporary As Int32
   Private gDevModeData As IntPtr
#End Region
#Region "Data structure"
   <StructLayout(LayoutKind.Sequential)> _
   Public Structure PRINTER_DEFAULTS
      Public pDatatype As Integer
      Public pDevMode As Integer
      Public DesiredAccess As Integer
   End Structure

   <StructLayout(LayoutKind.Sequential)> _
   Private Structure PRINTER_INFO_2
      <MarshalAs(UnmanagedType.LPStr)> Public pServerName As String
      <MarshalAs(UnmanagedType.LPStr)> Public pPrinterName As String
      <MarshalAs(UnmanagedType.LPStr)> Public pShareName As String
      <MarshalAs(UnmanagedType.LPStr)> Public pPortName As String
      <MarshalAs(UnmanagedType.LPStr)> Public pDriverName As String
      <MarshalAs(UnmanagedType.LPStr)> Public pComment As String
      <MarshalAs(UnmanagedType.LPStr)> Public pLocation As String
      Public pDevMode As IntPtr
      <MarshalAs(UnmanagedType.LPStr)> Public pSepFile As String
      <MarshalAs(UnmanagedType.LPStr)> Public pPrintProcessor As String
      <MarshalAs(UnmanagedType.LPStr)> Public pDatatype As String
      <MarshalAs(UnmanagedType.LPStr)> Public pParameters As String
      Public pSecurityDescriptor As IntPtr
      Public Attributes As Int32
      Public Priority As Int32
      Public DefaultPriority As Int32
      Public StartTime As Int32
      Public UntilTime As Int32
      Public Status As Int32
      Public cJobs As Int32
      Public AveragePPM As Int32
   End Structure

   Private Const CCDEVICENAME As Short = 32
   Private Const CCFORMNAME As Short = 32

   <StructLayout(LayoutKind.Sequential)> _
   Public Structure DEVMODE
      <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCDEVICENAME)> _
      Public dmDeviceName As String
      Public dmSpecVersion As Short
      Public dmDriverVersion As Short
      Public dmSize As Short
      Public dmDriverExtra As Short
      Public dmFields As Integer
      Public dmOrientation As Short
      Public dmPaperSize As Short
      Public dmPaperLength As Short
      Public dmPaperWidth As Short
      Public dmScale As Short
      Public dmCopies As Short
      Public dmDefaultSource As Short
      Public dmPrintQuality As Short
      Public dmColor As Short
      Public dmDuplex As Short
      Public dmYResolution As Short
      Public dmTTOption As Short
      Public dmCollate As Short
      <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCFORMNAME)> _
      Public dmFormName As String
      Public dmUnusedPadding As Short
      Public dmBitsPerPel As Short
      Public dmPelsWidth As Integer
      Public dmPelsHeight As Integer
      Public dmDisplayFlags As Integer
      Public dmDisplayFrequency As Integer
   End Structure

#End Region
#Region "Win API Def"
   <DllImport("kernel32.dll", EntryPoint:="GetLastError", SetLastError:=False, _
   ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
   Private Shared Function GetLastError() As Int32

   End Function

   <DllImport("winspool.Drv", EntryPoint:="ClosePrinter", SetLastError:=True, _
   ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
   Private Shared Function ClosePrinter(ByVal hPrinter As IntPtr) As Boolean

   End Function

   <DllImport("winspool.Drv", EntryPoint:="DocumentPropertiesA", SetLastError:=True, _
   ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
   Private Shared Function DocumentProperties(ByVal hwnd As IntPtr, ByVal hPrinter As IntPtr, _
    <MarshalAs(UnmanagedType.LPStr)> ByVal pDeviceNameg As String, _
   ByVal pDevModeOutput As IntPtr, ByRef pDevModeInput As IntPtr, ByVal fMode As Integer) As Integer

   End Function

   <DllImport("winspool.Drv", EntryPoint:="GetPrinterA", SetLastError:=True, _
       CharSet:=CharSet.Ansi, ExactSpelling:=True, _
       CallingConvention:=CallingConvention.StdCall)> _
   Private Shared Function GetPrinter(ByVal hPrinter As IntPtr, ByVal dwLevel As Int32, _
   ByVal pPrinter As IntPtr, ByVal dwBuf As Int32, ByRef dwNeeded As Int32) As Boolean
   End Function

   <DllImport("winspool.Drv", EntryPoint:="OpenPrinterA", _
       SetLastError:=True, CharSet:=CharSet.Ansi, _
       ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
   Private Shared Function OpenPrinter(<MarshalAs(UnmanagedType.LPStr)> ByVal szPrinter As String, _
       ByRef hPrinter As IntPtr, ByRef pd As PRINTER_DEFAULTS) As Boolean

   End Function

   <DllImport("winspool.drv", CharSet:=CharSet.Ansi, SetLastError:=True)> _
   Private Shared Function SetPrinter(ByVal hPrinter As IntPtr, ByVal Level As Integer, _
                                      ByVal pPrinter As IntPtr, ByVal Command As Integer) As Boolean

   End Function
#End Region

#Region "Constants"
   Private Const DM_DUPLEX As Integer = 4096 '0x1000
   Private Const DM_IN_BUFFER As Integer = 8
   Private Const DM_OUT_BUFFER As Integer = 2
   Private Const PRINTER_ACCESS_ADMINISTER As Integer = 4 '0x4
   Private Const STANDARD_RIGHTS_REQUIRED As Integer = 983040 '0xF0000
   Private Const PRINTER_ALL_ACCESS As Integer = STANDARD_RIGHTS_REQUIRED Or PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE
   Private Const PRINTER_ACCESS_USE As Integer = 8 '0x8
#End Region

#Region "Function to change printer settings"
   Public Function ChangePrinterSetting(ByVal iPrinterName As String) As Boolean
      gDevMode = Me.GetPrinterSettings(iPrinterName)
      Marshal.StructureToPtr(gDevMode, gDevModeData, True)
      gPInfo.pDevMode = gDevModeData
      gPInfo.pSecurityDescriptor = IntPtr.Zero
      'bring up the printer preferences dialog
      DocumentProperties(IntPtr.Zero, gPrinter, iPrinterName, gDevModeData _
          , gPInfo.pDevMode, DM_IN_BUFFER Or DM_OUT_BUFFER Or PRINTER_ACCESS_ADMINISTER)
      'update driver dependent part of the DEVMODE 
      Marshal.StructureToPtr(gPInfo, gPtrPrinterInfo, True)
      gLastError = Marshal.GetLastWin32Error()
      gNRet = Convert.ToInt16(SetPrinter(gPrinter, 2, gPtrPrinterInfo, 0))
      If gNRet = 0 Then
         'Unable to set shared printer settings.
         gLastError = Marshal.GetLastWin32Error()
         Throw New Exception(Marshal.GetLastWin32Error().ToString)
      End If
      If gPrinter <> IntPtr.Zero Then
         ClosePrinter(gPrinter)
      End If
      Return Convert.ToBoolean(gNRet)
   End Function

   Private Function GetPrinterSettings(ByVal PrinterName As String) As DEVMODE
      Dim lDevMode As DEVMODE
      gPrinterValues.pDatatype = 0
      gPrinterValues.pDevMode = 0
      gPrinterValues.DesiredAccess = PRINTER_ALL_ACCESS
      gNRet = Convert.ToInt32(OpenPrinter(PrinterName, _
                     gPrinter, gPrinterValues))
      If gNRet = 0 Then
         gLastError = Marshal.GetLastWin32Error()
         Throw New Exception(Marshal.GetLastWin32Error().ToString())
      End If

      GetPrinter(gPrinter, 2, IntPtr.Zero, 0, gNBytesNeeded)
      If gNBytesNeeded <= 0 Then
         Throw New System.Exception("Unable to allocate memory")
      Else
         ' Allocate enough space for PRINTER_INFO_2... 
         gPtrPrinterInfo = Marshal.AllocCoTaskMem(gNBytesNeeded)
         gPtrPrinterInfo = Marshal.AllocHGlobal(gNBytesNeeded)
         'The second GetPrinter fills in all the current settings, so all you 
         'need to do is modify what you're interested in...
         gNRet = Convert.ToInt32(GetPrinter(gPrinter, 2, _
             gPtrPrinterInfo, gNBytesNeeded, gNTemporary))
         If gNRet = 0 Then
            gLastError = Marshal.GetLastWin32Error()
            Throw New Exception(Marshal.GetLastWin32Error().ToString())
         End If
         gPInfo = CType(Marshal.PtrToStructure(gPtrPrinterInfo, GetType(PRINTER_INFO_2)), PRINTER_INFO_2)
         Dim lTempBuffer As IntPtr = New IntPtr()
         If gPInfo.pDevMode = IntPtr.Zero Then
            'If GetPrinter didn't fill in the DEVMODE, try to get it by calling
            'DocumentProperties...
            Dim ptrZero As IntPtr = IntPtr.Zero
            'get the size of the devmode structure
            gSizeOfDevMode = DocumentProperties(IntPtr.Zero, gPrinter, _
                               PrinterName, ptrZero, ptrZero, 0)
            gPtrDevMode = Marshal.AllocCoTaskMem(gSizeOfDevMode)
            Dim i As Integer = DocumentProperties(IntPtr.Zero, gPrinter, PrinterName, gPtrDevMode, _
            ptrZero, DM_OUT_BUFFER)
            If (i < 0) OrElse (gPtrDevMode <> IntPtr.Zero) Then
               'Cannot get the DEVMODE structure.
               Throw New System.Exception("Cannot get DEVMODE data")
            End If
            gPInfo.pDevMode = gPtrDevMode
         End If
         gIntError = DocumentProperties(IntPtr.Zero, gPrinter, _
                   PrinterName, IntPtr.Zero, lTempBuffer, 0)
         gDevModeData = Marshal.AllocHGlobal(gIntError)
         gIntError = DocumentProperties(IntPtr.Zero, gPrinter, _
                  PrinterName, gDevModeData, lTempBuffer, 2)
         lDevMode = CType(Marshal.PtrToStructure(gDevModeData, GetType(DEVMODE)), DEVMODE)
         If (gNRet = 0) OrElse (gPrinter = IntPtr.Zero) Then
            gLastError = Marshal.GetLastWin32Error()
            Throw New Exception(Marshal.GetLastWin32Error().ToString())
         End If
         Return lDevMode
      End If
   End Function
#End Region
End Class

and to use this class:

 Dim lPrinterpreferences As PrinterSettingsHelper = New PrinterSettingsHelper()
         lPrinterpreferences.ChangePrinterSetting("MyFavouritePrinter")
Ando