Virtual Developer Workshop: Containerized Development with Docker

Environment: Visual C++ 6.0, Windows NT 4.0 / Windows2000

Recently we had to do some printing on our IIS-Server. Printing on a physically existing printer was no problem. But after that we tried to print into files and send the files to the appropriate user.
Printing to "FILE:"-port caused the spooler-service to ask for a destination file, which you had to type in manually. Of course this was not the right approach for our IIS-server.

After studying the MSDN I found "The Print Monitor API" (refer to this for further information) which explains how to implement a print monitor. A print monitor is a DLL that exports some specific functions to enable the print spooler to do customized tasks--for example to print not to a printer but into a directory.

I used the print monitor example from the Microsoft DDK as a template to implement this print monitor. The example in the DDK was very complicated and did more than I wanted it to do (it should just redirect the print jobs into appropriate files).

Notice that the file WINSPLP.H was shipped with the Microsoft Windows NT 4.0 DDK.


The heart of the our monitor is the CPortList class. It...
...stores all ports in the registry.
...loads the ports from the registry at start-up of the spooler-service.
...holds all handles to opened files.
...supplies a list of all known ports to MyEnumPorts.

Refer to source code for detailed information.


It supplies all functions that are exported from the DLL:

Called by the spooler-service to obtain pointers to all the other functions, and to do some init tasks, of course.

Called by the spooler-service to obtain a list of the ports of our print monitor

Called by the spooler-service to open the port

Called by the spooler-service to start the printing of a print job. This will create a new file in the ports output directory and keep a handle to the opened file.

Called by the spooler-service to write to the port. This will write some data to the file. Will be called more than once.

Supplied, but not implemented (no need for)

Called by the spooler-service to write to the port. Called after all the data is written to the file. This will close the file.

Called by the spooler-service to close the port

Called by the spooler service to add a new port. The user has to specify the output directory.

supplied, but not implemented (no need for)

I added this function to give any application the possibility to wait for finishing a print job. This function expects as a parameter the complete path of the file you are waiting for. This function will return when the file has been completely written.

Notice that the path consists of one byte characters, to simplify interaction with Visual Basic.


To install the DLL just...
1. Copy the DLL to the System32-Directory.
2. Add following entries to the registry:
New key:
"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\Local Directory Port"
New value:
"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\Local Directory Port\Driver" = "dirport.dll"
3. Restart your Spooler-Service

Thank you for reading this.
Any comments appreciated.



Download demo project - 23 Kb
Download source - 12 Kb


  • Error: Specified port cannot be added ,win7

    Posted by Alex Jiang on 10/30/2014 05:35am

    At add printer port, I take error message "Specified port cannot be added. 0x00000578" on win7. Am I doing something wrong.But xp is OK . Why?

  • Error: Specified port cannot be added

    Posted by AYFERK on 04/15/2010 06:18pm

    At add printer port, I take error message "Specified port cannot be added. The request is not supportted" on 2003 server. Am I doing something wrong.

  • Print monitor that prints into a directory Win Vista

    Posted by gcontreras on 01/08/2009 12:22am

    What would be changed to run on Windows Vista.
    Please, help me.

  • Pausing a Print job under Win 98/Me

    Posted by Legacy on 02/11/2004 08:00am

    Originally posted by: Emilian

    I am writing a port monitor application that also display an custom dialog .
    All of the required port monitor functions(StartDocPort, WritePort, etc.) were implemented.
    The custom dialog is called on the EndDocPort() port monitor's function.
    Depending on the return code from the custom dialog I want to pause the current print job with:
    SetJob(hPrinter, JobId, 0, NULL, JOB_CONTROL_PAUSE);

    After that I am closing printer, release memory, etc.

    The same code is working under Win NT/2000 & XP (the print job remain on the spooler as paused)!
    On Win 98/Me the print job appear to be paused (I verifyed by displaying an messageBox right after SetJob - and go to the selected printer window - the current document is paused - the "Document -> Pause" check box is checked) but right after that the print job is automatically deleted by the spooler and I cannot see it anymore!

    I there a way to pause a print job programatically from the EndDocPort() function under Win 98 ?

    Any help will be very appreciated!
    Best Regards

  • Win98/2K/XP/NT working print driver

    Posted by Legacy on 10/21/2003 07:00am

    Originally posted by: Jaisor

    This solutiuon is awesome for win2K/XP but on other versions of Microsoft's "examplary" OS it tends to not work.

    I loved this solutuion for its simplicity and flexability and have used for a while, but now I had to make it work on win98 and I had hell for days.

    For all the people struggling with windows 98 port monitors, here is a project that is written with extreme detail and high understanding of the OS differences.

    It is free and even comes with the source.


  • How can I add a port from code?

    Posted by Legacy on 10/07/2003 07:00am

    Originally posted by: Giuliano


    I would like to install a port from code (C++) without the user interface, there's someone that can tell me how to do this?
    I tryed with the AddPortEx suggested by Mick; the function return TRUE but does't work with this Print Monitor.

    Thanks in advance,

  • vb6 printing

    Posted by Legacy on 08/28/2003 07:00am

    Originally posted by: robert

    hi, all
    i'am having a problem printing. i'm working on a project to add address and then print them on mailing labels. please help if you can.

    1.) the program excepts address ie; name,city and state, zipcode, into an arrary (user defind type)

    2.) within the program i select address for a certin zipcode and send them to print to the printer. this works find, my problem is that the formatting of the labels.

    3.) when the printing starts and the 1st sheet is finish the paper ejects and starts another sheet, how do i set the printer head position to the location where it was on the first sheet.

    4.) i've tried CurrentY and Printer.Print to add the spaces need. Printer.Print works ok but when the new page starts the format is off, i can't get the second page to start printing where the first page start printing on the labels.

    please help
    thank you very much

    here is and example of codes:

    dim a(1 to 3) as string
    dim b as integer
    For b = 1 to 17
    Printer.Print a
    Next a

    On the sheet of labels there are only five (5) labels. after the program prints the first five (5) labels and the next sheet feeds to the printer the formatting is off.

  • PortExists returns false but should be true

    Posted by Legacy on 08/21/2003 07:00am

    Originally posted by: Martijn Remmen

    I have a question about the dirport program. When I add a Local Directory Port this port is named after the directory where to store the files. When I do that again, and choose the same directory, no message is given. However, the printerselect screen does not appear.

    On another PC, the correct MessageBox is given, stating 'The port cannot be added, there is already a port with that name'. And no printer can be chosen (correctly).

    I have debugged the Monitor.cpp fie and I have discovered that the EnumPorts function returns the incorrect result.
    However this function is called by loading the spoolss.dll and then use GetAddress to get the address of the function. MSDN states that the EnumPorts function is included in winspool.h

    My question is why did you use the EnumPorts function the way you did? It seems that just include WinSpool.h would be an easier way? What could be the disadvantages of changing the way PortExists is implemented?

    Thanks in advance,

    Martijn Remmen

  • how to get the number of print copies for certain print job?

    Posted by Legacy on 08/18/2003 07:00am

    Originally posted by: sookee

    hi, i got few question to ask about the dirport.dll:
    1. how to get the number of print copies for certain print job?
    2. is it possible to filter the number of pages write in the ps file? Let say i print 5 pages, is it possible the program only copy the first 3 pages to the ps file and the rest of the pages just discard it?


  • Get the "Pause and restarting" status for printing web content

    Posted by Legacy on 08/14/2003 07:00am

    Originally posted by: sookee

    hi, when i print the web content to the printer, the web content page show "Pause and restarting" status and it unable to generate the ps file, but when i print the other file like microsoft word, it successfully generate the ps file. Any comment on how to fix it?

  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date