Results 1 to 19 of 19

Thread: Help!!! API to Intercept Print Jobs?

  1. #1

    Thread Starter
    New Member
    Join Date
    Oct 2000
    Location
    Posts
    2
    I have a network of about 55 computers (netware & NT) all running Win98 workstations. We have about 5 network printers and copiers to which a computer can print. What I'd like to do is build, if possible, a simple VB program to intercept what they are printing (from any application) and capture certain information about the print jobs before it gets sent off to the specified printer.

    For example, when a user selects print from the menu in MS Word, and after the Ok button is pressed on the printers dialog window, have my program then pop up asking the user to fill in specific information (who they want to charge for the print job...this is typically an internal billing code, etc.).

    The program would have to capture the name/driver of the printer users are printing to, the document they printed, number of pages, number of copies, date/time, etc.

    Is/are there Windows API's to help in my programming of this application? I've been, unsuccessfully, searching MSDN, web sites, etc. on how to intercept/detect a print job. Any help would be greatly appreciated!

    Thanks ahead of time...

  2. #2
    Fanatic Member gwdash's Avatar
    Join Date
    Aug 2000
    Location
    Minnesota
    Posts
    666
    some idea's:
    • Use EnumJobs and a timer, i will provide some code if nessecary. Not a good way
    • Use C++ and build your own print monitor/driver(no clue how, just know it's very difficult)
    • If NT Workstations then use FindFirstPrinterChangeNotification & related functions(must have NT Workstation)

    hope this helps
    GWDASH
    [b]VB6, Perl, ASP, HTML, JavaScript, VBScript, SQL, C, C++, Linux , Java, PHP, MySQL, XML[b]

  3. #3

    Thread Starter
    New Member
    Join Date
    Oct 2000
    Location
    Posts
    2
    Thanks for the info GW... If you would be so kind as to post some code to get me going, I'd appreciate it. I'm resigned to VB (v6) now, but would eventally like to move to C++, if possible. I know VB and very little of C.

    Thanks again.

    Steve

  4. #4
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411

    I might take a look at the project for you

    And I'll only ask for 1% of the estimated recovery your company makes due to correct billing codes

    hehe

    If you work for Lawyers or Accountants, that should make them balk..hehe

    Anyhow I'll let you know if I can do it. Now where did that win32API book go?..

    Regards
    Paul Lewis

  5. #5
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411

    Right then

    OK I had a look and the API's needed are not that bad to use. To extract the user or machine name that submitted the job will take some time (structure not in api32.txt so I have to create them carefully lest I crash)

    The reason we would need the machine name is to determine if the job just added to the queue was in fact this pc's.

    Do all your PC's have computer names? I assume they do and that there are no duplicates. Tell me if this is not the case OK? I can't use username because if the user is logged in on two machines, they would get the prompt on both!

    I'll let you know if a few hours how I have managed to get on. if someone posts you a solution before me, then all the better right ?



    Paul Lewis

  6. #6
    Fanatic Member gwdash's Avatar
    Join Date
    Aug 2000
    Location
    Minnesota
    Posts
    666
    To Add to your discussion, see http://www.vbapi.com and the GetUserName Function. For Machine, useGetComputerName function.

    Had to Change, wrong function name!!


    [Edited by gwdash on 10-17-2000 at 10:24 PM]
    GWDASH
    [b]VB6, Perl, ASP, HTML, JavaScript, VBScript, SQL, C, C++, Linux , Java, PHP, MySQL, XML[b]

  7. #7
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411

    damnation

    Curses to the FindFirstPrinterChangeNotification API!

    When I specify some PRINTER_NOTIFY_INFO in the call, I get back a handle of -1 and GetLastError returns 0. So I have no idea why it doesn't work. If I specify 0 (thats ok according to the documentation), I get a valid handle, but it seems to be for all queues on the network. In any case, it's for more printers than the one I am monitoring because it is right next to me. Unless people add jobs and delete them immediately or something...

    Anyhow, I am writing this in case someone happens to be able to guess what's wrong, and to let SteveLG know that my intended solution is not yet ready! So far I can tell when jobs get added or deleted but I can't get any info about the job at all...

    Grrrrr

    Paul Lewis

  8. #8
    Fanatic Member gwdash's Avatar
    Join Date
    Aug 2000
    Location
    Minnesota
    Posts
    666
    Could you PLEASE give me the FindFirstPrinterChange Notification Code. I need it for an app i'm writing. I've been all over looking for it!!!

    Just what you have so far is fine. if you do finish it, though, could you please send it to Me!!!
    GWDASH
    [b]VB6, Perl, ASP, HTML, JavaScript, VBScript, SQL, C, C++, Linux , Java, PHP, MySQL, XML[b]

  9. #9
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411
    Here you are. Please let me know if you figure out the problem I have on the network.

    I stuck with the code and finally worked out where I was going wrong although it was only a guess. It seems that I need to learn a bit more about the structure of a VB array as opposed to a C array.

    In any case, the code works to this point :
    Successfully sees any job related notification on the local queue (probably works also on any network queue that is not Netware!)

    I only extract the printer name, user name and machine name ans status string for now. You can also get many other bits of info like bytes printed, total pages etc.

    Yet to be done: Well there is alot to do! And these things are just for the spooler side of life.

    1) use the JOB STATUS constants to work out what each stats is telling us (Easy)

    2) respond to system events involving printers (probably hard - I ahven't looked at it yet). Currently, if the user prints to a printer we are not watching, then of course, we won't know.
    Also, if a printer we ARE watching is renamed or deleted, we don't get to know about it. There is an API to use for this as well.

    So without further ado, here is my progress to date (I am pleased to at least be able to deliver working code even though it is quite unsatisfactory in parts).

    Cheers

    Code:
    VERSION 5.00
    Begin VB.Form frmSpy 
       Caption         =   "Spooler Spy"
       ClientHeight    =   2595
       ClientLeft      =   1095
       ClientTop       =   1515
       ClientWidth     =   4485
       LinkTopic       =   "Form1"
       PaletteMode     =   1  'UseZOrder
       ScaleHeight     =   2595
       ScaleWidth      =   4485
       Begin VB.CommandButton cmdExit 
          Caption         =   "Exit"
          Height          =   375
          Left            =   1560
          TabIndex        =   2
          Top             =   1680
          Width           =   1095
       End
       Begin VB.CommandButton cmdSpy 
          Caption         =   "Spy"
          Height          =   375
          Left            =   1560
          TabIndex        =   0
          Top             =   240
          Width           =   1095
       End
       Begin VB.Label lblWatching 
          Height          =   255
          Left            =   840
          TabIndex        =   1
          Top             =   960
          Width           =   2655
       End
    End
    Attribute VB_Name = "frmSpy"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    Option Explicit
    
    ' Types with Suffix 2 are from Dan Appleman's API book but I think they are wrong
    Private Type PRINTER_NOTIFY_OPTIONS2
        Version As Long
        Flags As Long
        Count As Long
        pTypes As Long
    End Type
    
    Private Type PRINTER_NOTIFY_OPTIONS_TYPE2
        Type As Integer
        Reserved0 As Integer
        Reserved1 As Long
        Reserved2 As Long
        Count As Long
        pFields As Long
    End Type
    
    Private Type PRINTER_NOTIFY_INFO2
        Version As Long
        Flags As Long
        Count As Long
    End Type
    
    Private Type PRINTER_NOTIFY_INFO_DATA2
        Type As Integer
        Field As Integer
        Reserved As Long
        Id As Long
        Buf As Long
    End Type
    
    ' suffix 1 are from MSDN documentation
    Private Type PRINTER_NOTIFY_INFO_DATA1
        Type As Integer
        Field As Integer
        Reserved As Long
        Id As Long
        cbBuf As Long  ' part of union also awData[0]
        pBuf As Long   ' part of union
    End Type
    
    Private Type PRINTER_NOTIFY_OPTIONS_TYPE1
        Type As Integer
        Reserved0 As Integer
        Reserved1 As Long
        Reserved2 As Long
        Count As Long
        pFields As Long ' to array of Integer
    End Type
    
    Private Type PRINTER_NOTIFY_OPTIONS1
        Version As Long
        Flags As Long
        Count As Long
        pTypes As Long  'to array of PRINTER_NOTIFY_OPTIONS_TYPE
    End Type
    
    Private Type PRINTER_NOTIFY_INFO1
        Version As Long
        Flags As Long
        Count As Long
        aData As Long ' to array of PRINTER_NOTIFY_INFO_DATA
    End Type
    
    ' this is also form Dan's book...conflicting?
    ' but it's the winner
    Private Type PRINTER_NOTIFY_INFO3
      Version As Long
      Flags As Long
      Count As Long
      aData(4) As PRINTER_NOTIFY_INFO_DATA1  ' Varies
      ' this works but only because I have specified adata(4) here when
      ' I know I am only asking for 3 pnid's
      ' I need to learn how to use a pointer to a VB array which is what adata
    End Type
    
    Private Type PRINTER_DEFAULTS
            pDatatype As String
            pDevMode As Long
            DesiredAccess As Long
    End Type
    
    
    Private Const PRINTER_CHANGE_ADD_FORM = &H10000
    Private Const PRINTER_CHANGE_ADD_JOB = &H100
    Private Const PRINTER_CHANGE_ADD_PORT = &H100000
    Private Const PRINTER_CHANGE_ADD_PRINT_PROCESSOR = &H1000000
    Private Const PRINTER_CHANGE_ADD_PRINTER = &H1
    Private Const PRINTER_CHANGE_ADD_PRINTER_DRIVER = &H10000000
    Private Const PRINTER_CHANGE_ALL = &H7777FFFF
    Private Const PRINTER_CHANGE_CONFIGURE_PORT = &H200000
    Private Const PRINTER_CHANGE_DELETE_FORM = &H40000
    Private Const PRINTER_CHANGE_DELETE_JOB = &H400
    Private Const PRINTER_CHANGE_DELETE_PORT = &H400000
    Private Const PRINTER_CHANGE_DELETE_PRINT_PROCESSOR = &H4000000
    Private Const PRINTER_CHANGE_DELETE_PRINTER = &H4
    Private Const PRINTER_CHANGE_DELETE_PRINTER_DRIVER = &H40000000
    Private Const PRINTER_CHANGE_FAILED_CONNECTION_PRINTER = &H8
    Private Const PRINTER_CHANGE_FORM = &H70000
    Private Const PRINTER_CHANGE_JOB = &HFF00
    Private Const PRINTER_CHANGE_PORT = &H700000
    Private Const PRINTER_CHANGE_PRINT_PROCESSOR = &H7000000
    Private Const PRINTER_CHANGE_PRINTER = &HFF
    Private Const PRINTER_CHANGE_PRINTER_DRIVER = &H70000000
    Private Const PRINTER_CHANGE_SET_FORM = &H20000
    Private Const PRINTER_CHANGE_SET_JOB = &H200
    Private Const PRINTER_CHANGE_SET_PRINTER = &H2
    Private Const PRINTER_CHANGE_SET_PRINTER_DRIVER = &H20000000
    Private Const PRINTER_CHANGE_TIMEOUT = &H80000000
    Private Const PRINTER_CHANGE_WRITE_JOB = &H800
    
    
    
    Private Const WAIT_FAILED = -1&
    Private Const WAIT_OBJECT_0 = 0
    Private Const WAIT_ABANDONED = &H80&
    Private Const WAIT_ABANDONED_0 = &H80&
    Private Const WAIT_TIMEOUT = &H102&
    Private Const WAIT_IO_COMPLETION = &HC0&
    Private Const STILL_ACTIVE = &H103&
    Private Const INFINITE = -1&
    Private Const STANDARD_RIGHTS_REQUIRED = &HF0000
    Private Const PRINTER_ACCESS_ADMINISTER = &H4
    Private Const PRINTER_ACCESS_USE = &H8
    Private Const PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE)
    
    Private Const PRINTER_NOTIFY_FIELD_SERVER_NAME = &H0
    Private Const PRINTER_NOTIFY_FIELD_PRINTER_NAME = &H1
    Private Const PRINTER_NOTIFY_FIELD_SHARE_NAME = &H2
    Private Const PRINTER_NOTIFY_FIELD_PORT_NAME = &H3
    Private Const PRINTER_NOTIFY_FIELD_DRIVER_NAME = &H4
    Private Const PRINTER_NOTIFY_FIELD_COMMENT = &H5
    Private Const PRINTER_NOTIFY_FIELD_LOCATION = &H6
    Private Const PRINTER_NOTIFY_FIELD_DEVMODE = &H7
    Private Const PRINTER_NOTIFY_FIELD_SEPFILE = &H8
    Private Const PRINTER_NOTIFY_FIELD_PRINT_PROCESSOR = &H9
    Private Const PRINTER_NOTIFY_FIELD_PARAMETERS = &HA
    Private Const PRINTER_NOTIFY_FIELD_DATATYPE = &HB
    Private Const PRINTER_NOTIFY_FIELD_SECURITY_DESCRIPTOR = &HC
    Private Const PRINTER_NOTIFY_FIELD_ATTRIBUTES = &HD
    Private Const PRINTER_NOTIFY_FIELD_PRIORITY = &HE
    Private Const PRINTER_NOTIFY_FIELD_DEFAULT_PRIORITY = &HF
    Private Const PRINTER_NOTIFY_FIELD_START_TIME = &H10
    Private Const PRINTER_NOTIFY_FIELD_UNTIL_TIME = &H11
    Private Const PRINTER_NOTIFY_FIELD_STATUS = &H12
    Private Const PRINTER_NOTIFY_FIELD_STATUS_STRING = &H13
    Private Const PRINTER_NOTIFY_FIELD_CJOBS = &H14
    Private Const PRINTER_NOTIFY_FIELD_AVERAGE_PPM = &H15
    Private Const PRINTER_NOTIFY_FIELD_TOTAL_PAGES = &H16
    Private Const PRINTER_NOTIFY_FIELD_PAGES_PRINTED = &H17
    Private Const PRINTER_NOTIFY_FIELD_TOTAL_BYTES = &H18
    Private Const PRINTER_NOTIFY_FIELD_BYTES_PRINTED = &H19
    
    Private Const JOB_NOTIFY_FIELD_PRINTER_NAME = &H0
    Private Const JOB_NOTIFY_FIELD_MACHINE_NAME = &H1
    Private Const JOB_NOTIFY_FIELD_PORT_NAME = &H2
    Private Const JOB_NOTIFY_FIELD_USER_NAME = &H3
    Private Const JOB_NOTIFY_FIELD_NOTIFY_NAME = &H4
    Private Const JOB_NOTIFY_FIELD_DATATYPE = &H5
    Private Const JOB_NOTIFY_FIELD_PRINT_PROCESSOR = &H6
    Private Const JOB_NOTIFY_FIELD_PARAMETERS = &H7
    Private Const JOB_NOTIFY_FIELD_DRIVER_NAME = &H8
    Private Const JOB_NOTIFY_FIELD_DEVMODE = &H9
    Private Const JOB_NOTIFY_FIELD_STATUS = &HA
    Private Const JOB_NOTIFY_FIELD_STATUS_STRING = &HB
    Private Const JOB_NOTIFY_FIELD_SECURITY_DESCRIPTOR = &HC
    Private Const JOB_NOTIFY_FIELD_DOCUMENT = &HD
    Private Const JOB_NOTIFY_FIELD_PRIORITY = &HE
    Private Const JOB_NOTIFY_FIELD_POSITION = &HF
    Private Const JOB_NOTIFY_FIELD_SUBMITTED = &H10
    Private Const JOB_NOTIFY_FIELD_START_TIME = &H11
    Private Const JOB_NOTIFY_FIELD_UNTIL_TIME = &H12
    Private Const JOB_NOTIFY_FIELD_TIME = &H13
    Private Const JOB_NOTIFY_FIELD_TOTAL_PAGES = &H14
    Private Const JOB_NOTIFY_FIELD_PAGES_PRINTED = &H15
    Private Const JOB_NOTIFY_FIELD_TOTAL_BYTES = &H16
    Private Const JOB_NOTIFY_FIELD_BYTES_PRINTED = &H17
    Private Const PRINTER_NOTIFY_TYPE = &H0
    Private Const JOB_NOTIFY_TYPE = &H1
    Private Const INVALID_HANDLE_VALUE = -1
    Private Const PRINTER_NOTIFY_OPTIONS_REFRESH = &H1
    Private Const PRINTER_NOTIFY_INFO_DISCARDED = &H1
    
    Private Const JOB_STATUS_BLOCKED_DEVQ = &H200
    Private Const JOB_STATUS_DELETED = &H100
    Private Const JOB_STATUS_DELETING = &H4
    Private Const JOB_STATUS_ERROR = &H2
    Private Const JOB_STATUS_OFFLINE = &H20
    Private Const JOB_STATUS_PAPEROUT = &H40
    Private Const JOB_STATUS_PAUSED = &H1
    Private Const JOB_STATUS_PRINTED = &H80
    Private Const JOB_STATUS_PRINTING = &H10
    Private Const JOB_STATUS_RESTART = &H800
    Private Const JOB_STATUS_SPOOLING = &H8
    Private Const JOB_STATUS_USER_INTERVENTION = &H10000
    
    
    
    Private Declare Function FindFirstPrinterChangeNotification& Lib "winspool.drv" (ByVal hPrinter As Long, ByVal fdwFlags As Long, ByVal fdwOptions As Long, ByVal pPrinterNotifyOptions As Long)
    Private Declare Function FindNextPrinterChangeNotification& Lib "winspool.drv" (ByVal hChange As Long, pdwChange As Long, ByVal pPrinterNotifyOptions As Long, ppPrinterNotifyInfo As Long)
    Private Declare Function FindClosePrinterChangeNotification& Lib "winspool.drv" (ByVal hChange As Long)
    Private Declare Function FreePrinterNotifyInfo Lib "winspool.drv" (ByVal addr As Long) As Long
    Private Declare Function OpenPrinter& Lib "winspool.drv" Alias "OpenPrinterA" (ByVal pPrinterName As String, phPrinter As Long, pDefault As PRINTER_DEFAULTS)
    Private Declare Function ClosePrinter& Lib "winspool.drv" (ByVal hPrinter As Long)
    Private Declare Function WaitForSingleObject& Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long)
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    Private Declare Function GetLastError Lib "kernel32" () As Long
    Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Ptr() As Any) As Long
    
    Dim hNotification As Long
    Dim hPrinter As Long
    Dim quitFlag As Boolean
    Dim watching As Boolean
    Dim s1 As String, s2 As String
    
    
    
    Private Sub cmdExit_Click()
      quitFlag = True
      If Not watching Then Unload Me
    End Sub
    
    Private Sub cmdSpy_Click()
      watching = True
      lblWatching.Caption = watching
      Dim res As Long
      Dim lFlags As Long
      Dim x As Long
      Dim c As Integer
      Dim mbres As Long
      mbres = vbOK
      
      Dim pDefault As PRINTER_DEFAULTS
      With pDefault
        .pDatatype = vbNullString
        .pDevMode = 0
        .DesiredAccess = PRINTER_ACCESS_USE
      End With
      
      If hPrinter <> 0 Then Call ClosePrinter(hPrinter)
      
      res = OpenPrinter(Printer.DeviceName, hPrinter, pDefault)
      
      If res = 0 Then
        Debug.Print "Unable to open printer " & Printer.DeviceName & " " & GetLastError
        Exit Sub
      Else
        Debug.Print "Open printer " & Printer.DeviceName
      End If
      
      Dim pni As PRINTER_NOTIFY_INFO3
      Dim pnid() As PRINTER_NOTIFY_INFO_DATA1
      Dim pnot As PRINTER_NOTIFY_OPTIONS_TYPE1
      Dim pno As PRINTER_NOTIFY_OPTIONS1
      Dim myData(4) As PRINTER_NOTIFY_INFO_DATA1
      Dim myOptions(0) As PRINTER_NOTIFY_OPTIONS_TYPE1
      Dim myInfo(4) As Integer
      Dim ppni As Long, ppno As Long
      
      myInfo(0) = JOB_NOTIFY_FIELD_MACHINE_NAME
      myInfo(1) = JOB_NOTIFY_FIELD_USER_NAME
      myInfo(2) = JOB_NOTIFY_FIELD_PRINTER_NAME
      myInfo(3) = JOB_NOTIFY_FIELD_TOTAL_PAGES
      myInfo(4) = JOB_NOTIFY_FIELD_STATUS
      
      With myOptions(0)
        .Type = JOB_NOTIFY_TYPE
        .Count = 5
        .pFields = VarPtr(myInfo(0))
      End With
      
      With myData(0)
        .Type = JOB_NOTIFY_TYPE
        .Field = JOB_NOTIFY_FIELD_MACHINE_NAME
      End With
      
      With myData(1)
        .Type = JOB_NOTIFY_TYPE
        .Field = JOB_NOTIFY_FIELD_USER_NAME
      End With
      
      With myData(2)
        .Type = JOB_NOTIFY_TYPE
        .Field = JOB_NOTIFY_FIELD_PRINTER_NAME
      End With
      
      With myData(3)
        .Type = JOB_NOTIFY_TYPE
        .Field = JOB_NOTIFY_FIELD_PAGES_PRINTED
      End With
      
      With myData(4)
        .Type = JOB_NOTIFY_TYPE
        .Field = JOB_NOTIFY_FIELD_STATUS_STRING
      End With
      
      
      With pno
        .Count = 1
        .Version = 2
        .pTypes = VarPtr(myOptions(0))
        .Flags = PRINTER_NOTIFY_OPTIONS_REFRESH
      End With
      
      
      ppno = VarPtr(pno)
      
      ' option 1
      ' OK
      'hNotification = FindFirstPrinterChangeNotification(hPrinter, PRINTER_CHANGE_JOB, 0, 0)
      
      'option2 was crap so it's gone -
      
      'option 3
      ' results in dll error 120 (This function is not supported on this system. )
      ' OK when not using network printer
      hNotification = FindFirstPrinterChangeNotification(hPrinter, 0, 0, ppno)
      
      'option 4
      ' results in dll error 120 (This function is not supported on this system. )
      ' OK when not using network printer
      'hNotification = FindFirstPrinterChangeNotification(hPrinter, PRINTER_CHANGE_JOB, 0, ppno)
      
      If hNotification = INVALID_HANDLE_VALUE Then
        Debug.Print "Unable to create notification object " & GetLastError
        watching = False
        lblWatching.Caption = watching
        Exit Sub
      End If
    
    
      Do
        ' currently, with option 1 above, I get a signal of -256 every 10 seconds!
        ' which is not documented.  I assume somehing on the network is doing this
        ' perhaps to keep the queue refreshed or something??
        
        ' my printer queue in the office is called "\\AKTHSE1\THSE11_4SI_MP_UX"
        ' when a real job is queued, I don't get a signal
          
        ' stop the press!  it works fine for a printer I set up locally (FILE:)
        ' so it must be to do with the network or the Netware queue.
        ' Continuing with tests on local queue instead.
        
        res = WaitForSingleObject(hNotification, 500)
        If res = WAIT_TIMEOUT Then
          ' do nothing. set quitflag if you want to
        Else
          res = FindNextPrinterChangeNotification(hNotification, lFlags, 0, ppni)
          Debug.Print res, lFlags, GetLastError
          
          If (lFlags And PRINTER_CHANGE_ADD_JOB) = PRINTER_CHANGE_ADD_JOB Then
              Debug.Print Now & " Job was added"
          End If
          
          If (lFlags And PRINTER_CHANGE_WRITE_JOB) = PRINTER_CHANGE_WRITE_JOB Then
              Debug.Print Now & " Job was written"
          End If
          
          If (lFlags And PRINTER_CHANGE_DELETE_JOB) = PRINTER_CHANGE_DELETE_JOB Then
              Debug.Print Now & " Job was deleted"
          End If
          
          If ppni <> 0 Then
            CopyMemory ByVal (VarPtr(pni)), ByVal ppni, Len(pni)
            Debug.Print "pni:", "Flags", "Count", "Version", "aData"
            Debug.Print "pni:", pni.Flags, pni.Count, pni.Version, VarPtr(pni.aData(0))
            If pni.Count > 0 Then
              'ReDim pnid(pni.Count - 1)
              ' tricky stuff. aData is not a pointer but the first
              ' byte of the array of pnid
              ' I might have a problem here because the pBuf seems
              ' to point to a buffer, but I can't get the string out of it
              'CopyMemory pnid(0), addr, Len(pnid(0)) * pni.Count
              
              ' attempt # 2 - maybe the aData points to a safearray?
              ' darn documentation is hard to follow
              pnid = pni.aData 'will this work?
              
              If (pni.Flags And PRINTER_NOTIFY_INFO_DISCARDED) = PRINTER_NOTIFY_INFO_DISCARDED Then
                Debug.Print "Some data was discarded "
                ' to be done: use PRINTER_NOTIFY_OPTIONS_REFRESH to get the discarded data
              End If
              
              Debug.Print "pnid : ", "Type", "Field", "Id", "cbBuf", "pBuf"
              For c = 0 To UBound(pnid)
                Debug.Print "pnid : ", pnid(c).Type, pnid(c).Field, pnid(c).Id, pnid(c).cbBuf, pnid(c).pBuf,
                PrintPrinterNotifyInfoData pnid(c)
              Next
            End If
            res = FreePrinterNotifyInfo(ppni)
          End If
        
        End If
        DoEvents
      Loop While Not quitFlag
      
      watching = False
      lblWatching.Caption = watching
        
      If quitFlag Then Unload Me
        
    End Sub
    
    Private Sub PrintPrinterNotifyInfoData(pnid As PRINTER_NOTIFY_INFO_DATA1)
      With pnid
        Select Case .Type
        Case PRINTER_NOTIFY_TYPE
        
        Case JOB_NOTIFY_TYPE
          Select Case .Field
            Case JOB_NOTIFY_FIELD_PRINTER_NAME
              Debug.Print "Printer Name: " & GetStringFromLPSTR(.pBuf, .cbBuf),
              
            Case JOB_NOTIFY_FIELD_MACHINE_NAME
              Debug.Print "Machine Name: " & GetStringFromLPSTR(.pBuf, .cbBuf),
              
            Case JOB_NOTIFY_FIELD_USER_NAME
              Debug.Print "User Name: " & GetStringFromLPSTR(.pBuf, .cbBuf),
              
            Case JOB_NOTIFY_FIELD_STATUS_STRING
              Debug.Print "Status: " & GetStringFromLPSTR(.pBuf, .cbBuf),
              
            Case JOB_NOTIFY_FIELD_PAGES_PRINTED
              Debug.Print "Pages Printed: " & CStr(.cbBuf),
              
            Case JOB_NOTIFY_FIELD_TOTAL_BYTES
              Debug.Print "Total Bytes: " & CStr(.cbBuf),
              
            Case JOB_NOTIFY_FIELD_TOTAL_PAGES
              Debug.Print "Total Pages: " & CStr(.cbBuf),
              
            Case JOB_NOTIFY_FIELD_STATUS
              Debug.Print "Status: " & CStr(.cbBuf),
              
            Case Else
              Debug.Print "Unparsed filed value = " & .Field,
          End Select
        End Select
      End With
      Debug.Print
    End Sub
    
    Private Function GetStringFromLPSTR(ByVal lpstr As Long, ByVal chars As Long) As String
      Dim myStringRes As String
      Dim bString() As Byte
      Dim c As Long
      Dim pos1 As Long
      c = 40
      ' lpstr is wide string that is null terminated
      ' I've messed around in here to try to get it working
      ' but it doesn't look like a string in here at all...
      
      ' finally working I think...
      If chars <= 0 Then Exit Function
      ReDim bString(chars - 1)
      
      CopyMemory bString(0), ByVal lpstr, chars
      myStringRes = StrConv(bString, vbUnicode)
      GetStringFromLPSTR = Left(myStringRes, InStr(1, myStringRes, Chr(0)))
      
    End Function
    
    Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
      If Not quitFlag And watching Then
        quitFlag = True
        Cancel = True
      End If
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
      If hNotification <> 0 Then FindClosePrinterChangeNotification (hNotification)
      If hPrinter <> 0 Then ClosePrinter hPrinter
    End Sub
    Let me know if you find it useful..
    Paul Lewis

  10. #10
    Guest
    This is some cool code...

    I'm in need of something similar, have a look at my problem and let me please know what you think of it?

    Problem: In a school, students are printing, and now have to pay an amount of money for each color/black&white print.
    They pay this when they pick up the paper from the printer, where now somebody is sitting al day. (Price is $0.10 for BW and $0.25 for color but that's not what this is about). What I'd like to do is capture the printjob before it goes to the printer. Then, on a "billing" computer students must use their school-passes to enter a valid code and or password, and when this code is valid it is send to the server as a "go on, print it". If not, the print-job will be dropped after some time (say 5 mins). The "bills" will be printed monthly or so...

    In short: Student prints, goes to computer to enter his/her code and the print comes out. If code is invalid or a "time-out" occurs, the printjob is cancelled.

    The part of the billing-computer, communication with the server and so on I can do. But for catching the printjobs and "holding them" is where I depend on you guys! I'm hoping you'd take a thought about my problem and tell me if you'd like to help me solve it... (I'd like to help solving too ... )

    Any ideas or suggestions this far?

  11. #11
    Guest
    I just had a (great) idea: Is it possible using VB to write my own "printer-driver"? If so, I could use the student's computers to send the printjob to my driver, pop-up a window (or share the printer on the "billing-computer") and make them enter the code. Then I could "forward" the printjob to the desired printer. This way I'd be in full control over the print-job...

    Any ideas?

  12. #12
    Fanatic Member gwdash's Avatar
    Join Date
    Aug 2000
    Location
    Minnesota
    Posts
    666
    You can use the API to Register A "Monitor" to do that, but i don't think it can be done in VB
    GWDASH
    [b]VB6, Perl, ASP, HTML, JavaScript, VBScript, SQL, C, C++, Linux , Java, PHP, MySQL, XML[b]

  13. #13
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411

    Change of mind

    After rwading about Print Monitors I realise that this is not the way to go because a Print Monitor works between the spooler and the printer. By then, the helpful user with the info they need to tell us have gone...

    Also, my previous attempt is of limited use as well for the same reason.

    So I've started to look at PrintHookProc. Setting up a hook so that the instant the user click Print In any application (and to any printer) on that machine, then our code can run. For monitored network printers we could popup a dialog asking for certain information. If they don't provide it then we don't pass on the print command.

    It's an idea only and it is a little advance for me at present. I have to go off and read about hooking in general so if anyone else is game enough to have a look, then let us know please

    Cheers
    Paul Lewis

  14. #14
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411

    Was this thread of use toi anyone?

    SteveLG, gwdash?

    Just wondering.

    Regards
    Paul Lewis

  15. #15
    Member
    Join Date
    Jul 2000
    Posts
    37

    this has me thinking

    OK, now that i'm totally confused and brain storming like crazy. I start to wonder...

    Will any of this work on a local PC & printer? If I wanted a simple app to keep track of everything going to the printer, could i use this code, or would i have to make any changes in it?

    BTW.. Anyone know the name of the driver that controls that spooler dialog?

  16. #16
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411

    Smile Locally works fine

    Cheers
    Paul Lewis

  17. #17
    Fanatic Member gwdash's Avatar
    Join Date
    Aug 2000
    Location
    Minnesota
    Posts
    666
    It needs NT, Correct
    GWDASH
    [b]VB6, Perl, ASP, HTML, JavaScript, VBScript, SQL, C, C++, Linux , Java, PHP, MySQL, XML[b]

  18. #18
    New Member
    Join Date
    Oct 2000
    Posts
    2

    Arrow

    SteveLG:

    There's a neat program called 'RedMon' (Redirection Port Monitor), that comes with the Win32 port of Ghostscript, which might be handy for what you're trying to do.

    It basically takes a print job within Win95/98/NT and feeds it into the stdin of a program, such as Ghostscript. It also comes with a program called 'redrun', which copies its stdio (the printed document) to a temporary file on disk and then runs another program. Like a print manager you're attempting to create.

    Here's the URL for redrun:

    http://www.cs.wisc.edu/~ghost/redmon/index.html

    There's a few other interesting tidbits on the page, including information on how to set up RedMon and Ghostscript to write .pdf files, as a free alternative to Adobe's PDFWriter software.

    Hope this helps.

  19. #19
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    672

    Re: Help!!! API to Intercept Print Jobs?

    Quote Originally Posted by ;145719
    This is some cool code...

    I'm in need of something similar, have a look at my problem and let me please know what you think of it?

    Problem: In a school, students are printing, and now have to pay an amount of money for each color/black&white print.
    They pay this when they pick up the paper from the printer, where now somebody is sitting al day. (Price is $0.10 for BW and $0.25 for color but that's not what this is about). What I'd like to do is capture the printjob before it goes to the printer. Then, on a "billing" computer students must use their school-passes to enter a valid code and or password, and when this code is valid it is send to the server as a "go on, print it". If not, the print-job will be dropped after some time (say 5 mins). The "bills" will be printed monthly or so...

    In short: Student prints, goes to computer to enter his/her code and the print comes out. If code is invalid or a "time-out" occurs, the printjob is cancelled.

    The part of the billing-computer, communication with the server and so on I can do. But for catching the printjobs and "holding them" is where I depend on you guys! I'm hoping you'd take a thought about my problem and tell me if you'd like to help me solve it... (I'd like to help solving too ... )

    Any ideas or suggestions this far?
    My suggestion is to use a web server. The client passes the document, and the print command, the server accepts the document, prints it. Returns the print success. The way the type cloud prints.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width