我在 Windows 7 上访问和更改打印机设置时遇到了严重问题。
Option Strict On
Imports System.Runtime.InteropServices
Imports System.ComponentModel
<StructLayout(LayoutKind.Sequential)> _
Structure PRINTER_DEFAULTS
Public pDatatype As IntPtr
Public pDevMode As IntPtr
Public DesiredAccess As Integer
End Structure
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Public Structure PRINTER_INFO_2
<MarshalAs(UnmanagedType.LPTStr)> _
Public pServerName As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pPrinterName As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pShareName As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pPortName As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pDriverName As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pComment As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pLocation As String
Public pDevMode As IntPtr
<MarshalAs(UnmanagedType.LPTStr)> _
Public pSepFile As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pPrintProcessor As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pDatatype As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public pParameters As String
Public pSecurityDescriptor As IntPtr
Public Attributes As UInteger
Public Priority As UInteger
Public DefaultPriority As UInteger
Public StartTime As UInteger
Public UntilTime As UInteger
Public Status As UInteger
Public cJobs As UInteger
Public AveragePPM As UInteger
End Structure
Structure POINTL
Public x As Int32
Public y As Int32
End Structure
<Flags()> _
Enum DM As Integer
Orientation = &H1
PaperSize = &H2
PaperLength = &H4
PaperWidth = &H8
Scale = &H10
Position = &H20
NUP = &H40
DisplayOrientation = &H80
Copies = &H100
DefaultSource = &H200
PrintQuality = &H400
Color = &H800
Duplex = &H1000
YResolution = &H2000
TTOption = &H4000
Collate = &H8000
FormName = &H10000
LogPixels = &H20000
BitsPerPixel = &H40000
PelsWidth = &H80000
PelsHeight = &H100000
DisplayFlags = &H200000
DisplayFrequency = &H400000
ICMMethod = &H800000
ICMIntent = &H1000000
MediaType = &H2000000
DitherType = &H4000000
PanningWidth = &H8000000
PanningHeight = &H10000000
DisplayFixedOutput = &H20000000
End Enum
<StructLayout(LayoutKind.Explicit, CharSet:=CharSet.Ansi)> _
Structure DEVMODE
Public Const CCHDEVICENAME As Integer = 32
Public Const CCHFORMNAME As Integer = 32
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHDEVICENAME)> _
<System.Runtime.InteropServices.FieldOffset(0)> _
Public dmDeviceName As String
<System.Runtime.InteropServices.FieldOffset(32)> _
Public dmSpecVersion As Int16
<System.Runtime.InteropServices.FieldOffset(34)> _
Public dmDriverVersion As Int16
<System.Runtime.InteropServices.FieldOffset(36)> _
Public dmSize As Int16
<System.Runtime.InteropServices.FieldOffset(38)> _
Public dmDriverExtra As Int16
<System.Runtime.InteropServices.FieldOffset(40)> _
Public dmFields As DM
<System.Runtime.InteropServices.FieldOffset(44)> _
Private dmOrientation As Int16
<System.Runtime.InteropServices.FieldOffset(46)> _
Private dmPaperSize As Int16
<System.Runtime.InteropServices.FieldOffset(48)> _
Private dmPaperLength As Int16
<System.Runtime.InteropServices.FieldOffset(50)> _
Private dmPaperWidth As Int16
<System.Runtime.InteropServices.FieldOffset(52)> _
Public dmScale As Int16
<System.Runtime.InteropServices.FieldOffset(54)> _
Public dmCopies As Int16
<System.Runtime.InteropServices.FieldOffset(56)> _
Private dmDefaultSource As Int16
<System.Runtime.InteropServices.FieldOffset(58)> _
Private dmPrintQuality As Int16
<System.Runtime.InteropServices.FieldOffset(44)> _
Public dmPosition As POINTL
<System.Runtime.InteropServices.FieldOffset(52)> _
Public dmDisplayOrientation As Int32
<System.Runtime.InteropServices.FieldOffset(56)> _
Public dmDisplayFixedOutput As Int32
<System.Runtime.InteropServices.FieldOffset(60)> _
Public dmColor As Short
<System.Runtime.InteropServices.FieldOffset(62)> _
Public dmDuplex As Short
<System.Runtime.InteropServices.FieldOffset(64)> _
Public dmYResolution As Short
<System.Runtime.InteropServices.FieldOffset(66)> _
Public dmTTOption As Short
<System.Runtime.InteropServices.FieldOffset(68)> _
Public dmCollate As Short
<System.Runtime.InteropServices.FieldOffset(72)> _
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHFORMNAME)> _
Public dmFormName As String
<System.Runtime.InteropServices.FieldOffset(102)> _
Public dmLogPixels As Int16
<System.Runtime.InteropServices.FieldOffset(104)> _
Public dmBitsPerPel As Int32
<System.Runtime.InteropServices.FieldOffset(108)> _
Public dmPelsWidth As Int32
<System.Runtime.InteropServices.FieldOffset(112)> _
Public dmPelsHeight As Int32
<System.Runtime.InteropServices.FieldOffset(116)> _
Public dmDisplayFlags As Int32
<System.Runtime.InteropServices.FieldOffset(116)> _
Public dmNup As Int32
<System.Runtime.InteropServices.FieldOffset(120)> _
Public dmDisplayFrequency As Int32
End Structure
Class PrinterSettings
Private gPrinter As IntPtr = New System.IntPtr()
Private gPrinterValues As New PRINTER_DEFAULTS()
Private gPInfo As 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 Integer
Private gDevModeData As IntPtr
<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 pDeviceName As String, ByVal pDevModeOutput As IntPtr, ByVal pDevModeInput As IntPtr, ByVal fMode As Integer) As Integer
End Function
<DllImport("winspool.drv", SetLastError:=True, CharSet:=CharSet.Auto)> _
Public 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
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
Public Function SetPrinterSettings(ByVal iPrinterName As String, ByVal dm As DEVMODE, Optional ByVal ShowPreferences As Integer = 0) As Integer
gDevMode = dm
Marshal.StructureToPtr(gDevMode, gDevModeData, True)
gPInfo.pDevMode = gDevModeData
gPInfo.pSecurityDescriptor = IntPtr.Zero
''optionally brings up the printer preferences dialog
If ShowPreferences = 1 Then
Dim retval As Integer
retval = DocumentProperties(IntPtr.Zero, gPrinter, iPrinterName, gDevModeData, gPInfo.pDevMode, DM_IN_BUFFER Or DM_OUT_BUFFER Or PRINTER_ACCESS_ADMINISTER)
If retval = DialogResult.Cancel Then Return -1
End If
''update driver dependent part of the DEVMODE
''HERE CRASHES ******************************
Marshal.StructureToPtr(gPInfo, gPtrPrinterInfo, True)
''
gLastError = Marshal.GetLastWin32Error()
gNRet = Convert.ToInt16(SetPrinter(gPrinter, 2, gPtrPrinterInfo, 0))
If gNRet = 0 Then
''Unable to set extern printer settings.
gLastError = Marshal.GetLastWin32Error()
Throw New Win32Exception(gLastError)
End If
If gPrinter <> IntPtr.Zero Then
ClosePrinter(gPrinter)
End If
Return CInt(gNRet)
End Function
Public Function GetPrinterSettings(ByVal PrinterName As String) As DEVMODE
Dim lDevMode As DEVMODE
gPrinterValues.pDatatype = IntPtr.Zero
gPrinterValues.pDevMode = IntPtr.Zero
gPrinterValues.DesiredAccess = PRINTER_ALL_ACCESS
gNRet = Convert.ToInt32(OpenPrinter(PrinterName, gPrinter, gPrinterValues))
If gNRet = 0 Then
gLastError = Marshal.GetLastWin32Error()
Throw New Win32Exception(gLastError)
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 youre interested in...
gNRet = Convert.ToInt32(GetPrinter(gPrinter, 2, gPtrPrinterInfo, gNBytesNeeded, gNTemporary))
If gNRet = 0 Then
gLastError = Marshal.GetLastWin32Error()
Throw New Win32Exception(gLastError)
End If
gPInfo = CType(Marshal.PtrToStructure(gPtrPrinterInfo, GetType(PRINTER_INFO_2)), PRINTER_INFO_2)
Dim lTempBuffer As New IntPtr()
If gPInfo.pDevMode = IntPtr.Zero Then
''if GetPrinter didnt fill in the DEVMODE, try to get it by calling
''DocumentProperties...
Dim ptrZero As IntPtr = IntPtr.Zero
''get the size of the devmode struct
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 struct.
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 Win32Exception(gLastError)
End If
Return lDevMode
End If
End Function
End Class
用法:
Dim defaultPrinterName As String = getDefaultPrinter()
Dim dm As DEVMODE
Dim lPrinterpreferences As PrinterSettings = New PrinterSettings()
dm = lPrinterpreferences.GetPrinterSettings(defaultPrinterName)
dm.dmCopies = 4
lPrinterpreferences.SetPrinterSettings(defaultPrinterName, dm, 1)
编辑:当调用 SetPrinter API 时,可能最后一个错误仍然存在。
gNRet = Convert.ToInt16(SetPrinter(gPrinter, 2, gPtrPrinterInfo, 0))
这会引发错误:“打印机驱动程序未知”,但是
gNRet = Convert.ToInt16(SetPrinter(gPrinter, 0, gPtrPrinterInfo, 3))
工作没有错误但不可靠。
如何摆脱最后一个错误?