【问题标题】:Trouble printing to a specific Tray (PaperSource) using VB.NET PrinterSettings使用 VB.NET PrinterSettings 打印到特定托盘 (PaperSource) 时遇到问题
【发布时间】:2021-09-17 11:35:56
【问题描述】:

我在使用 VB.NET 自动选择要在特定打印机(HP OfficeJet Pro 9020 系列)上打印的纸盘(PaperSource)时遇到了问题。我已将我的代码减少到最低限度,以表明我正在遵循的过程。

在弹出打印对话框并从列表中选择“纸盘 2”后。代码行prn.PrinterSettings = pd.PrinterSettings 应用该设置,然后从正确的纸盘打印。但是,如果您只是在从错误托盘打印的对话框上单击确定。

奇怪的是,如您所见,我在代码中设置了“Tray 2”选项,所以理论上该对象应该完全相同。我使用Newtonsoft.JSON 序列化pd.PrinterSettings 对象之前和之后我在对话框中选择了“托盘2”,这样我就可以比较差异并且它们都是相同的。最初的唯一区别是我之前在代码中提到的那些。他们没有任何区别。那些存在;

pd.PrinterSettings.PrintFileName = "IP_192.168.1.50"
pd.PrinterSettings.PrinterName = "Dispatch Office"
pd.PrinterSettings.Collate = False

但无论出于何种原因,它只有在我明确打开对话框并选择所需的托盘并单击确定时才有效。 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

【问题讨论】:

  • 您正在更改DefaultPageSettings,这是打印机对话框向用户显示内容的基线,而不是最终结果。
  • @GSerg 这看起来确实很奇怪,但这是唯一的选择。没有PageSettings 选项。此外,请参阅 Microsoft 文档,其中建议以这种方式完成。 docs.microsoft.com/en-us/dotnet/api/…
  • @GSerg 为了清楚起见,我在原始帖子的末尾添加了另一个代码块。感谢您的帮助。
  • 您提到使用 json 序列化比较对象;您是否尝试过在代码中放置断点并检查监视窗口中的对象?监视窗口可以查看实际类型(不仅仅是静态类型)和私有成员,因此您可能可以在那里找到未序列化的内容。
  • 没有 PaperSourceKind 类型的 Tray 2 。如果需要为 PrintDocument 提供自定义 PaperSourcePaperSize,则需要构建自定义对象并指定 KindRawKind 属性。否则,请使用标准枚举选择现有的论文来源之一(您无法编造名称,请使用 PaperSourceKind 枚举器)。

标签: .net vb.net printing


【解决方案1】:

下午,

我相信我自己已经找到了解决方案。它非常复杂,并且围绕着 .PrinterSettings 对象中的 Devmode 结构展开。

这个答案要归功于我在 Code Project 中偶然发现的 Richard Dean。 查看此链接了解所有详细信息

https://www.codeproject.com/articles/488737/storing-and-recalling-printer-settings-in-csharp-n

我将项目移植到 VB.NET,然后将其简化到可以使用它来实现我想要的结果的程度。 Richard 编写的软件基本上允许您保存打印机使用的这种结构,并在以后调用它。我用他的应用程序保存了 .bin 文件,现在在下面的代码中引用该文件。

函数GetSettings返回一个链接到指定打印机的PrinterSettings对象,然后覆盖我之前保存的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 的帖子了解更多详情,因为它是一个非常有用且功能强大的工具。

问候,

亨利

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多