Click here to Skip to main content
15,886,664 members
Articles / Web Development / HTML

EMF Printer Spool File Viewer

Rate me:
Please Sign up or sign in to vote.
4.72/5 (32 votes)
23 Apr 2014CPOL3 min read 812.5K   25.3K   92  
A viewer application for EMF format spool files
Imports SpoolfileReaderBase
Imports System.IO
Imports System.Diagnostics

Public Class EMFSpoolfileReader
    Implements ISpoolfileReaderBase

#Region "Application trace switch"
    Public Shared ApplicationTracing As New System.Diagnostics.TraceSwitch("EMFSpoolfileReader", "EMF Spool File reader application tracing")
#End Region

#Region "Perfomance monitoring information"

    Private Const PERFORMANCE_COUNTER_NAME As String = "EMF Spoolfile Pages"
    Private Const PERFORMANCE_TIMER_NAME As String = "EMF Spoolfile Pages/sec"

    Private Const PERFORMANCE_COUNTER_CATEGORY As String = "Spoolfile Readers"

    Private Shared SpoolfileReaderPerformaceCounter As PerformanceCounter

    Private Sub InitialiseCounterCategory()
        '\\ Create the performace category if it is not already done
        If PerformanceCounterCategory.Exists(PERFORMANCE_COUNTER_CATEGORY) Then
            PerformanceCounterCategory.Delete(PERFORMANCE_COUNTER_CATEGORY)
        End If

        Dim SpoolerCounterCreationDataCollection As New CounterCreationDataCollection()
        Dim SpoolfileReaderCounterCreationDataCollection As New CounterCreationDataCollection()

        Dim SpoolfileReaderCounterCreationData As New CounterCreationData(PERFORMANCE_COUNTER_NAME, "Spoolfile pages read", PerformanceCounterType.NumberOfItems32)
        Dim SpoolfileReaderTimerCreationData As New CounterCreationData(PERFORMANCE_TIMER_NAME, "Spoolfile pages read second", PerformanceCounterType.NumberOfItems32)

        SpoolfileReaderCounterCreationDataCollection.Add(SpoolfileReaderCounterCreationData)
        SpoolfileReaderCounterCreationDataCollection.Add(SpoolfileReaderTimerCreationData)

        Try
            PerformanceCounterCategory.Create(PERFORMANCE_COUNTER_CATEGORY, "", System.Diagnostics.PerformanceCounterCategoryType.MultiInstance, SpoolerCounterCreationDataCollection)
        Catch ex As System.Security.SecurityException
            System.Diagnostics.Trace.TraceError(ex.ToString())
        End Try

    End Sub

    Private Sub InitialiseCounter()
        'SpoolfileReaderPerformaceCounter = New PerformanceCounter(PERFORMANCE_COUNTER_CATEGORY, PERFORMANCE_COUNTER_NAME, True)
    End Sub
#End Region

#Region "Private member variables"
    Private _Copies As Integer = 1 '\\ The number of copies per page
    Private _Pages As Integer = 0
    Private _EMFPages As New EMFPages()
#End Region

#Region "Private enumerated types"
    Private Enum SpoolerRecordTypes
        SRT_EOF = &H0                 ' // int32 zero
        SRT_RESERVED_1 = &H1        '*  1                                */
        SRT_FONTDATA = &H2       '  2 Font Data                      */
        SRT_DEVMODE = &H3       '  3 DevMode                        */
        SRT_FONT2 = &H4        '4 Font Data                      */
        SRT_RESERVED_5 = &H5         ' 5                                */
        SRT_FONT_MM = &H6        ' 6 Font Data (Multiple Master)    */
        SRT_FONT_SUB1 = &H7       '   7 Font Data (SubsetFont 1)       */
        SRT_FONT_SUB2 = &H8       '   8 Font Data (SubsetFont 2)      
        SRT_RESERVED_9 = &H9
        SRT_UNKNOWN = &H10            ' // int unknown...
        SRT_RESERVED_A = &HA
        SRT_RESERVED_B = &HB
        SRT_PAGE = &HC         ' 12  Enhanced Meta File (EMF)       */
        SRT_EOPAGE1 = &HD       ' 13  EndOfPage                      */
        SRT_EOPAGE2 = &HE       ' 14  EndOfPage                      */
        SRT_EXT_FONT = &HF        ' 15  Ext Font Data                  */
        SRT_EXT_FONT2 = &H10       ' 16  Ext Font Data                  */
        SRT_EXT_FONT_MM = &H11        ' 17  Ext Font Data (Multiple Master)
        SRT_EXT_FONT_SUB1 = &H12      ' 18  Ext Font Data (SubsetFont 1)   */
        SRT_EXT_FONT_SUB2 = &H13      '* 19  Ext Font Data (SubsetFont 2)   */
        SRT_EXT_PAGE = &H14           ' 20  Enhanced Meta File? 
        SRT_JOB_INFO = &H10000        ' // int length, wchar jobDescription
    End Enum
#End Region

#Region "Private type defs"
    Private Structure EMFMetaRecordHeader
        Dim Seek As Long
        Dim iType As SpoolerRecordTypes
        Dim nSize As Int32
    End Structure
#End Region

#Region "ISpoolerfileReaderBase implementation"
    Public Function GetTruePageCount(ByVal SpoolFilename As String) As Integer Implements ISpoolfileReaderBase.GetTruePageCount

        If ApplicationTracing.TraceVerbose Then
            Trace.WriteLine("GetTruePageCount for " & SpoolFilename, Me.GetType.ToString)
        End If


        '\\ The number of copies is held in the shadow file
        Dim ShadowFilename As String
        If Not Path.GetExtension(SpoolFilename).ToUpper = ".SHD" Then
            ShadowFilename = Path.ChangeExtension(SpoolFilename, ".SHD")
        Else
            ShadowFilename = SpoolFilename
        End If
        Dim fiShadow As New System.IO.FileInfo(ShadowFilename)
        If fiShadow.Exists Then
            Dim ShadowFileStream As New System.IO.FileStream(ShadowFilename, FileMode.Open, FileAccess.Read)
            Dim ShadowBinaryReader As New BinaryReader(ShadowFileStream)

            '\\ Close the shadow file 
            ShadowBinaryReader.Close()
            ShadowFileStream.Close()
        End If

        SpoolFilename = Path.ChangeExtension(SpoolFilename, ".SPL")

        '\\ Open a binary reader for the spool file
        Dim SpoolFileStream As New System.IO.FileStream(SpoolFilename, FileMode.Open, FileAccess.Read)
        Dim SpoolBinaryReader As New BinaryReader(SpoolFileStream, System.Text.Encoding.UTF8)

        'Read the spooler records and count the total pages
        Dim recNext As EMFMetaRecordHeader = NextHeader(SpoolBinaryReader)
        While recNext.iType <> SpoolerRecordTypes.SRT_EOF
            If recNext.iType = SpoolerRecordTypes.SRT_PAGE Then
                _Pages += 1
            End If
            'SpoolfileReaderPerformaceCounter.Increment()
            Call SkipAHeader(recNext, SpoolBinaryReader)
            recNext = NextHeader(SpoolBinaryReader)
        End While

        '\\ Close the spool file
        SpoolBinaryReader.Close()
        SpoolFileStream.Close()

        Return _Pages * _Copies
    End Function
#End Region

#Region "Private functions"
    Private Function NextHeader(ByRef SpoolBinaryReader As BinaryReader) As EMFMetaRecordHeader

        Dim recRet As New EMFMetaRecordHeader()

        With recRet
            '\\ get the record type
            .Seek = SpoolBinaryReader.BaseStream.Position
            Try
                .iType = CType(SpoolBinaryReader.ReadInt32, EMFSpoolfileReader.SpoolerRecordTypes)
            Catch e As EndOfStreamException
                .iType = SpoolerRecordTypes.SRT_EOF
                Exit Function
            End Try
            '\\ Get the record size
            .nSize = SpoolBinaryReader.ReadInt32
        End With
        Return recRet

    End Function

    Private Sub SkipAHeader(ByVal Header As EMFMetaRecordHeader, ByRef SpoolBinaryReader As BinaryReader)

        With Header
            If .nSize <= 0 Then
                .nSize = 8
            End If
            If .iType = SpoolerRecordTypes.SRT_JOB_INFO Then
                Dim JobInfo() As Byte
                JobInfo = SpoolBinaryReader.ReadBytes(.nSize)
                SpoolBinaryReader.BaseStream.Seek(.Seek + .nSize, SeekOrigin.Begin)
            ElseIf .iType = SpoolerRecordTypes.SRT_EOF Then
                '\\ End of file reached..do nothing
            ElseIf .iType = SpoolerRecordTypes.SRT_DEVMODE Then
                '\\ Spool job DEVMODE
                Dim _dmThis As New DevMode(SpoolBinaryReader)
                _Copies = _dmThis.Copies
                SpoolBinaryReader.BaseStream.Seek(.Seek + 8 + .nSize, SeekOrigin.Begin)
            ElseIf .iType = SpoolerRecordTypes.SRT_PAGE Or .iType = SpoolerRecordTypes.SRT_EXT_PAGE Then
                '\\ 
                Call ProcessEMFRecords(Header, SpoolBinaryReader)
            ElseIf .iType = SpoolerRecordTypes.SRT_EOPAGE1 Or .iType = SpoolerRecordTypes.SRT_EOPAGE2 Then
                '\\ int plus long
                Dim EOPAGE() As Byte
                EOPAGE = SpoolBinaryReader.ReadBytes(.nSize)
                If .nSize = &H8 Then
                    SpoolBinaryReader.BaseStream.Seek(.Seek + .nSize + 8, SeekOrigin.Begin)
                End If
            ElseIf .iType = SpoolerRecordTypes.SRT_UNKNOWN Then
                SpoolBinaryReader.BaseStream.Seek(.Seek + 4, SeekOrigin.Begin)
            Else
                SpoolBinaryReader.BaseStream.Seek(.Seek + .nSize, SeekOrigin.Begin)
            End If
        End With

    End Sub

    Private Sub ProcessEMFRecords(ByVal EMFRecord As EMFMetaRecordHeader, ByVal SpoolBinaryReader As BinaryReader)
        Dim nNextRecordStart As Long

        nNextRecordStart = EMFRecord.Seek + 8

        SpoolBinaryReader.BaseStream.Seek(nNextRecordStart, SeekOrigin.Begin)

        '\\ EMRMETAHEADER followed by other EMR records 
        Dim ThisPage As New EMFPage(SpoolBinaryReader)
        _EMFPages.Add(ThisPage)
        nNextRecordStart = nNextRecordStart + ThisPage.Header.FileSize
        SpoolBinaryReader.BaseStream.Seek(nNextRecordStart, SeekOrigin.Begin)

    End Sub
#End Region

#Region "Public interface"
    Public ReadOnly Property Pages() As EMFPages
        Get
            Return _EMFPages
        End Get
    End Property
#End Region

#Region "Public constructor"
    Public Sub New()
        Call InitialiseCounterCategory()
        Call InitialiseCounter()
    End Sub
#End Region
End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Ireland Ireland
C# / SQL Server developer
Microsoft MVP (Azure) 2017
Microsoft MVP (Visual Basic) 2006, 2007

Comments and Discussions