使用 VB.NET PrinterSettings 打印到特定纸盒 (PaperSource) 时出现问题
Trouble printing to a specific Tray (PaperSource) using VB.NET PrinterSettings
我在使用 VB.NET 从特定打印机(HP OfficeJet Pro 9020 系列)自动 select 打印托盘 (PaperSource) 时遇到问题。我已将我的代码减少到最低限度,以表明我正在遵循的过程。
打印对话框弹出后,您从列表中 select 'Tray 2'。代码行 prn.PrinterSettings = pd.PrinterSettings
应用该设置,随后它从正确的纸盘打印。但是,如果您只是在对话框中单击“确定”,它会从错误的纸盒中打印出来。
奇怪的是,如您所见,我在代码中设置了 'Tray 2' 选项,因此,理论上该对象应该完全相同。在对话框中 selected 'Tray 2' 之前和之后,我使用 Newtonsoft.JSON
序列化 pd.PrinterSettings
对象,这样我就可以比较差异并且它们都是相同的。一开始唯一的区别是我之前在代码中提到的那些。他们没有任何区别。那些是;
pd.PrinterSettings.PrintFileName = "IP_192.168.1.50"
pd.PrinterSettings.PrinterName = "Dispatch Office"
pd.PrinterSettings.Collate = False
但无论出于何种原因,它只有在我明确打开对话框和 select 所需的托盘并单击确定时才有效。 prn.PrinterSettings = pd.PrinterSettings
是唯一对使用的打印机有任何影响的代码行,因此 pd.PrinterSettings
中的某处一定缺少某些我在 JSON 字符串中看不到的东西。我在其他打印机上使用此代码取得了成功,但是这台特定打印机似乎有所不同,但我认为问题必须出在 VB.NET 上。
有没有人遇到过这样的事情?下面是我的完整代码。
Private Sub Print()
Dim pd As New PrintDialog
pd.PrinterSettings.PrintFileName = "IP_192.168.1.50"
pd.PrinterSettings.PrinterName = "Dispatch Office"
pd.PrinterSettings.Collate = False
For Each paperSource As Printing.PaperSource In pd.PrinterSettings.PaperSources
If paperSource.SourceName = "Tray 2" Then
pd.PrinterSettings.DefaultPageSettings.PaperSource = paperSource
Exit For
End If
Next
If pd.ShowDialog = DialogResult.OK Then
Dim prn As New Printing.PrintDocument
prn.PrinterSettings = pd.PrinterSettings
AddHandler prn.PrintPage, AddressOf Me.PrintPageHandler
prn.Print()
RemoveHandler prn.PrintPage, AddressOf Me.PrintPageHandler
End If
End Sub
Private Sub PrintPageHandler(ByVal sender As Object, ByVal args As Printing.PrintPageEventArgs)
Dim myFont As New Font("Courier New", 9)
Dim strTextFile = IO.File.ReadAllText("C:\textfile.txt")
args.Graphics.DrawString(strTextFile, New Font(myFont, FontStyle.Regular), Brushes.Black, 50, 50)
End Sub
另外,确认一下,这与打印对话框无关。
下面的代码可能是最短的形式也不起作用。
Private Sub PrintAuto()
Dim ps As New Printing.PrinterSettings With {.PrinterName = "Dispatch Office"}
ps.DefaultPageSettings.PaperSource = ps.PaperSources(3) ' Tray 2
Dim prn As New Printing.PrintDocument With {.PrinterSettings = ps}
AddHandler prn.PrintPage, AddressOf PrintPageHandler
prn.Print()
RemoveHandler prn.PrintPage, AddressOf PrintPageHandler
End Sub
大家下午好,
我相信我自己已经找到了解决这个问题的方法。它极其复杂,围绕 .PrinterSettings
对象中的 Devmode 结构展开。
这个答案要归功于我在 Code Project 上偶然发现的 Richard Dean。
查看此 link 了解所有详细信息
https://www.codeproject.com/articles/488737/storing-and-recalling-printer-settings-in-csharp-n
我接受了该项目并将其移植到 VB.NET,然后将其简化到可以使用它来实现我想要的结果的程度。 Richard 编写的软件基本上允许您保存打印机使用的这个结构并在以后调用它。我用他的应用程序保存了 .bin 文件,现在在我下面的代码中引用该文件。
函数GetSettings
returns一个PrinterSettings
对象linked到指定的打印机,然后覆盖我之前保存的Devmode数据。现在只需单击一下按钮即可打印到我打印机的纸盒 2。
Imports System.Runtime.InteropServices
Imports System.Drawing.Printing
Imports System.IO
Public Class GetPrinterSettings
<DllImport("kernel32.dll", ExactSpelling:=True)>
Public Shared Function GlobalFree(ByVal handle As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", ExactSpelling:=True)>
Public Shared Function GlobalLock(ByVal handle As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", ExactSpelling:=True)>
Public Shared Function GlobalUnlock(ByVal handle As IntPtr) As IntPtr
End Function
Public Function GetSettings(PrinterName As String) As PrinterSettings
Dim ps As PrinterSettings = New PrinterSettings()
ps.PrinterName = PrinterName
Dim Filename As String = "C:\Devmode.bin"
If File.Exists(Filename) Then
' Gets the data from the arraylist and loads it back into memory
' int mode
' 1 = Load devmode structure from file
' 2 = Load devmode structure from arraylist
Dim hDevMode As IntPtr = IntPtr.Zero ' a handle to our current DEVMODE
Dim pDevMode As IntPtr = IntPtr.Zero ' a pointer to our current DEVMODE
Dim TempArray As Byte()
Try
' Obtain the current DEVMODE position in memory
hDevMode = ps.GetHdevmode(ps.DefaultPageSettings)
' Obtain a lock on the handle and get an actual pointer so Windows won't move
' it around while we're messing with it
pDevMode = GlobalLock(hDevMode)
' Overwrite our current DEVMODE in memory with the one we saved.
' They should be the same size since we haven't like upgraded the OS
' Or anything.
' Load devmode structure from file
Dim fs As FileStream = New FileStream(Filename, FileMode.Open, FileAccess.Read)
TempArray = New Byte(fs.Length - 1) {}
fs.Read(TempArray, 0, TempArray.Length)
fs.Close()
fs.Dispose()
For i As Integer = 0 To TempArray.Length - 1
Marshal.WriteByte(pDevMode, i, TempArray(i))
Next
' We're done messing
GlobalUnlock(hDevMode)
' Tell our printer settings to use the one we just overwrote
ps.SetHdevmode(hDevMode)
ps.DefaultPageSettings.SetHdevmode(hDevMode)
' It's copied to our printer settings, so we can free the OS-level one
GlobalFree(hDevMode)
Catch ex As Exception
If hDevMode <> IntPtr.Zero Then
MessageBox.Show(ex.Message)
GlobalUnlock(hDevMode)
GlobalFree(hDevMode) ' And to boot, we don't need that DEVMODE anymore, either
hDevMode = IntPtr.Zero
End If
End Try
End If
Return ps
End Function
End Class
我希望这对同一条船上的任何人都有用。再次感谢 Richard 发布源代码。请查看 Richard 的 post 以获取更多详细信息,因为它是一个非常有用且功能强大的工具。
此致,
亨利
我在使用 VB.NET 从特定打印机(HP OfficeJet Pro 9020 系列)自动 select 打印托盘 (PaperSource) 时遇到问题。我已将我的代码减少到最低限度,以表明我正在遵循的过程。
打印对话框弹出后,您从列表中 select 'Tray 2'。代码行 prn.PrinterSettings = pd.PrinterSettings
应用该设置,随后它从正确的纸盘打印。但是,如果您只是在对话框中单击“确定”,它会从错误的纸盒中打印出来。
奇怪的是,如您所见,我在代码中设置了 'Tray 2' 选项,因此,理论上该对象应该完全相同。在对话框中 selected 'Tray 2' 之前和之后,我使用 Newtonsoft.JSON
序列化 pd.PrinterSettings
对象,这样我就可以比较差异并且它们都是相同的。一开始唯一的区别是我之前在代码中提到的那些。他们没有任何区别。那些是;
pd.PrinterSettings.PrintFileName = "IP_192.168.1.50"
pd.PrinterSettings.PrinterName = "Dispatch Office"
pd.PrinterSettings.Collate = False
但无论出于何种原因,它只有在我明确打开对话框和 select 所需的托盘并单击确定时才有效。 prn.PrinterSettings = pd.PrinterSettings
是唯一对使用的打印机有任何影响的代码行,因此 pd.PrinterSettings
中的某处一定缺少某些我在 JSON 字符串中看不到的东西。我在其他打印机上使用此代码取得了成功,但是这台特定打印机似乎有所不同,但我认为问题必须出在 VB.NET 上。
有没有人遇到过这样的事情?下面是我的完整代码。
Private Sub Print()
Dim pd As New PrintDialog
pd.PrinterSettings.PrintFileName = "IP_192.168.1.50"
pd.PrinterSettings.PrinterName = "Dispatch Office"
pd.PrinterSettings.Collate = False
For Each paperSource As Printing.PaperSource In pd.PrinterSettings.PaperSources
If paperSource.SourceName = "Tray 2" Then
pd.PrinterSettings.DefaultPageSettings.PaperSource = paperSource
Exit For
End If
Next
If pd.ShowDialog = DialogResult.OK Then
Dim prn As New Printing.PrintDocument
prn.PrinterSettings = pd.PrinterSettings
AddHandler prn.PrintPage, AddressOf Me.PrintPageHandler
prn.Print()
RemoveHandler prn.PrintPage, AddressOf Me.PrintPageHandler
End If
End Sub
Private Sub PrintPageHandler(ByVal sender As Object, ByVal args As Printing.PrintPageEventArgs)
Dim myFont As New Font("Courier New", 9)
Dim strTextFile = IO.File.ReadAllText("C:\textfile.txt")
args.Graphics.DrawString(strTextFile, New Font(myFont, FontStyle.Regular), Brushes.Black, 50, 50)
End Sub
另外,确认一下,这与打印对话框无关。 下面的代码可能是最短的形式也不起作用。
Private Sub PrintAuto()
Dim ps As New Printing.PrinterSettings With {.PrinterName = "Dispatch Office"}
ps.DefaultPageSettings.PaperSource = ps.PaperSources(3) ' Tray 2
Dim prn As New Printing.PrintDocument With {.PrinterSettings = ps}
AddHandler prn.PrintPage, AddressOf PrintPageHandler
prn.Print()
RemoveHandler prn.PrintPage, AddressOf PrintPageHandler
End Sub
大家下午好,
我相信我自己已经找到了解决这个问题的方法。它极其复杂,围绕 .PrinterSettings
对象中的 Devmode 结构展开。
这个答案要归功于我在 Code Project 上偶然发现的 Richard Dean。 查看此 link 了解所有详细信息
https://www.codeproject.com/articles/488737/storing-and-recalling-printer-settings-in-csharp-n
我接受了该项目并将其移植到 VB.NET,然后将其简化到可以使用它来实现我想要的结果的程度。 Richard 编写的软件基本上允许您保存打印机使用的这个结构并在以后调用它。我用他的应用程序保存了 .bin 文件,现在在我下面的代码中引用该文件。
函数GetSettings
returns一个PrinterSettings
对象linked到指定的打印机,然后覆盖我之前保存的Devmode数据。现在只需单击一下按钮即可打印到我打印机的纸盒 2。
Imports System.Runtime.InteropServices
Imports System.Drawing.Printing
Imports System.IO
Public Class GetPrinterSettings
<DllImport("kernel32.dll", ExactSpelling:=True)>
Public Shared Function GlobalFree(ByVal handle As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", ExactSpelling:=True)>
Public Shared Function GlobalLock(ByVal handle As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", ExactSpelling:=True)>
Public Shared Function GlobalUnlock(ByVal handle As IntPtr) As IntPtr
End Function
Public Function GetSettings(PrinterName As String) As PrinterSettings
Dim ps As PrinterSettings = New PrinterSettings()
ps.PrinterName = PrinterName
Dim Filename As String = "C:\Devmode.bin"
If File.Exists(Filename) Then
' Gets the data from the arraylist and loads it back into memory
' int mode
' 1 = Load devmode structure from file
' 2 = Load devmode structure from arraylist
Dim hDevMode As IntPtr = IntPtr.Zero ' a handle to our current DEVMODE
Dim pDevMode As IntPtr = IntPtr.Zero ' a pointer to our current DEVMODE
Dim TempArray As Byte()
Try
' Obtain the current DEVMODE position in memory
hDevMode = ps.GetHdevmode(ps.DefaultPageSettings)
' Obtain a lock on the handle and get an actual pointer so Windows won't move
' it around while we're messing with it
pDevMode = GlobalLock(hDevMode)
' Overwrite our current DEVMODE in memory with the one we saved.
' They should be the same size since we haven't like upgraded the OS
' Or anything.
' Load devmode structure from file
Dim fs As FileStream = New FileStream(Filename, FileMode.Open, FileAccess.Read)
TempArray = New Byte(fs.Length - 1) {}
fs.Read(TempArray, 0, TempArray.Length)
fs.Close()
fs.Dispose()
For i As Integer = 0 To TempArray.Length - 1
Marshal.WriteByte(pDevMode, i, TempArray(i))
Next
' We're done messing
GlobalUnlock(hDevMode)
' Tell our printer settings to use the one we just overwrote
ps.SetHdevmode(hDevMode)
ps.DefaultPageSettings.SetHdevmode(hDevMode)
' It's copied to our printer settings, so we can free the OS-level one
GlobalFree(hDevMode)
Catch ex As Exception
If hDevMode <> IntPtr.Zero Then
MessageBox.Show(ex.Message)
GlobalUnlock(hDevMode)
GlobalFree(hDevMode) ' And to boot, we don't need that DEVMODE anymore, either
hDevMode = IntPtr.Zero
End If
End Try
End If
Return ps
End Function
End Class
我希望这对同一条船上的任何人都有用。再次感谢 Richard 发布源代码。请查看 Richard 的 post 以获取更多详细信息,因为它是一个非常有用且功能强大的工具。
此致,
亨利