Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Implementing Wireless Print for WinNT/Win2K


November 2001/Implementing Wireless Print for WinNT/Win2K

A Windows NT/Windows 2000 print monitor is a DLL that, among other things, handles communications between the spooler and the print device. But if you need to support wireless network printers that communicate with proprietary hardware, you have to write your own print monitor.

In the January 2000 issue of Windows Developer’s Journal, I proposed a general setup utility called MonSet to automate the task of installing and removing a print monitor (see references), where I created a “Wireless Port” and attached a sample print monitor WLSMON for the MonSet test. Actually in my development, WLSMON was the prototype of a real wireless print monitor, although its design has not been involved in that article. Since then, I have received questions from many readers regarding the development of WLSMON, which motivated me to write this article to reveal the WLSMON’s architecture, including how to make a custom print monitor and supply wireless functionality to it.

Windows NT/Windows 2000 supports two types of print monitors: port monitors and language monitors. A port monitor is responsible for communication channels directly with the device driver. For example, the default port monitor, LOCALMON, supports parallel, serial, and file ports. A language monitor lets the spooler monitor the status of a bi-directional printer that supports two-way communication with the spooler. However, a language monitor never communicates directly with a printer port, which relies on the port monitor to send and receive printer data for it.

The WLSMON monitor is a port monitor with the structure similar to the LOCALMON (see references). In a wireless network, a device call depends on the particular implementation of underlying drivers, which is beyond LOCALMON’s scope. I created the special port (e.g. WLS1:) embedded in the spooler. I let the spooler take care of the upper part of the print job, maintaining its unified print interface. If the spooler recognizes the WLS1 port, it calls monitor-based functions in WLSMON to do the underlying wireless processing. Thus, applications with the Print command can print documents via WLS1 just like via LPT1, as shown in Figure 1.

In understanding this article, some background knowledge in port monitor and wireless communications is helpful. I’ve provided a few useful resources that offer a brief look at concepts of writing a print monitor, as well some valuable information about wireless communications (see references).

Creating the Wireless Monitor

A port represents the destination for the data being sent to the printer. To be able to print to a physical printer, Windows NT/Windows 2000 requires that the printer be connected to one or more ports that are controlled by port monitors. When the spooler has a printing job, it attempts to dispatch the job to the physical printer connected to the designated port by calling the associated monitor’s functions.

A wireless network, (usually known as wireless LAN) is composed of transceivers that transmit and receive radio frequency from each other with RF connections rather than through cables. Every hardware device, PC, or printer, is attached with a transceiver internally or externally, referred to as “PC Node” and “Printer Node” in Figure 2. Each node is assigned to a unique ID as an address number or as a node name string. For clarity, I use name IDs in this article.

WLSMON creates a new monitor type “Wireless Port” and names its port “WLS#.” In Figure 2, I illustrate an example of the wireless network, where we have n PCs, three WLS ports, and two printers, with names such as “Unit 1” and “WLS Printer 2.” The port WLS1 and WLS2 are associated to the “WLS Printer 1” that controls the physical printer “Generic.”

Consider how this mechanism (Figure 2) works. When you use PC1 to choose the “Generic/Text Only On WLS1:” printer (Figure 1) to print something, the spooler first invokes StartDocPort() in WLSMON to tell that it’s ready to send a print job. So StartDocPort() sets an RF connection between the “Unit 1” and the “WLS Printer 1” using the WLS1 port configurations. Then in the subsequent calls of WritePort(), the print data passed from the spooler are transferred on the connection. When the spooler finishes sending, it calls EndDocPort() to let WLSMON terminate the connection.

Practicing WLSMON Setup

To better understand the WLSMON monitor, let’s walk through its setup procedures on Windows 2000. Here are the steps you would follow to construct the picture in Figure 2:

  1. Use the utility MonSet.exe (the executable available online) to install WLSMON, by calling the similar command below with your correct path of WLSMON.DLL.
      MonSet "Wireless Port" c:\temp\WLSMON.DLL
  2. 2. Open the control panel Printers applet and select a printer, such as “Generic/Text Only.” Choose File|Properties and click on the Ports tab to open a dialog for “Generic/Text Only” as shown in Figure 3. Note that this is the final setting after the setup.
  3. Click the Add Port button to open the Printer Ports dialog as shown (the back window) in Figure 4, where you can see “Wireless Port” in the available port types list as the result of installation in step 1. Select “Wireless Port” and click the New Port button. The spooler invokes WLSMON’s AddPort() to let you enter a port name in another dialog (in the front of Figure 4).
  4. When you finish entering a port name and click the OK button, the spooler invokes WLSMON’s ConfigurePort() to pop up another dialog (Figure 5) to let you configure the port just added. This is the same dialog you will meet if you click the Configure Port button in Figure 3. There you select the “WLS Printer 1” to link to the WLS2 port and set the transmission retry timeout to 200. You may need to put more controls in the dialog to access extra settings in your implementations.
  5. Repeat step 3 to set three ports WLS1, WLS2, and WLS3. Now open RegEdit.exe to confirm the setup by the following registry,
    HKEY_LOCAL_MACHINE\
      SYSTEM\
        CurrentControlSet\
          Control\
            Print\
              Monitors\
                Wireless Port\
                  Driver = "WLSMON.DLL"

As shown in Figure 6, WLSMON creates the sub key Ports to store the settings for the available WLS ports, which reflect the picture in Figure 2.

Examining the WLSMON Monitor API

Any print monitor is required to export a set of predefined API functions called by the spooler in printing and port management. The API prototypes can be found in the DDK’s winsplp.h file. Table 1 contains the brief descriptions for the WLSMON API, which is implemented in wlsmon.c (Listing 1) and config.c (Listing 2).

In wlsmon.c, DllEntryPoint() and InitializePrintMonitor() are the only two functions that a monitor’s DLL must explicitly export, declared in wlsmon.def. The spooler obtains all other functions through a pointer table returned by InitializePrintMonitor(), and uses the function pointers to make subsequent calls into the monitor. Note that my function table is MonitorEx, where I disabled three unsupported entries to NULL, as OpenPortEx() is for a language monitor and the others are optional.

Another task in InitializePrintMonitor() is to establish a linked list for all available WLS ports in the registry. The function reads from the Ports key and calls the helper CreatePortEntry() to make a list of the following port data type.

typedef struct _INIPORT {       
    DWORD   signature;
    DWORD   cb;
    struct  _INIPORT *pNext;
    DWORD   cRef;
    LPWSTR  pName;          /* Port Name */
    HANDLE  hFile;               
    DWORD   cbWritten;
    DWORD   Status;              
    LPWSTR  pPrinterName;
    LPWSTR  pDeviceName;
    HANDLE  hPrinter;
    DWORD   JobId;
} INIPORT, *PINIPORT;

The next three functions take the responsibility for print job and each has an INIPORT handle as the first parameter. The main work in StartDocPort() is to set the required fields for the port handle passed by the spooler. Besides, I call WlsConnectPrinter() to set an RF connection between the local PC and the remote printer transceivers; if WlsConnectPrinter() succeeds, it returns another handle that is defined by the wireless device driver. I save the wireless handle to the hPrinter member, and later use this handle in WritePort() to send print data on the connection. EndDocPort() needs to do two cleanups, terminating the RF connection for the network and closing the logic printer for the spooler.

OpenPort() is called by the spooler whenever a port is assigned to a printer. It searches a port handle in the linked list for a match of the port pName, and returns this handle in the second parameter, so that the spooler can pass it to call other functions in printing. ClosePort() is called when no printer associates to the port. I make ReadPort() and SetPortTimeOuts() empty, for I can read print status directly from a duplex RF connection with a timeout in configuration.

Four port management functions reside in config.c (Listing 2). In AddPort(), the helper GetPortName() opens a port dialog (in Figure 4) and returns the port name. After validating the name, AddPort() lets you configure the port by calling ConfigurePort(), which in turn, opens a configuration dialog and saves the settings in the registry. All these functions are straightforward and readable without need of more explanations.

For details, refer to the source code in this month’s archive. dialogs.c gives two dialog procedures. Many helper functions are contained in util.c and config.c. The dialog and string definitions can be found in wlsmon.rc. The head file wlsmon.h lists the helpful prototypes in groups. I’ll cover the file wlsfnc.c more in the following section.

Placeholder for Wireless Procedures

I provided a placeholder for the wireless code named wlsfnc.c that encapsulates all the wireless procedures, like WlsConnectPrinter(). Typically, the procedures would work like those in Listing 3, where the functions prefixed with two underscores need further implementation based on technology. A developer can couple his own wireless driver support in __WlsXxx functions.

At first, you have to define a wireless data structure like __WLS_HANDLE in wlsfnc.c, which is passed between application layer and driver layer. In WlsConnectPrinter(), I initialize such a handle wlsHnd, by calling __WlsOpenHandle(). I retrieve the settings from the registry (a printer name and timeout in my sample), and then get a local name from the wireless network. Next, I set the required members in wlsHnd and then pass it to __WlsSetConnection(). Finally, if connection succeeds, I return the wlsHnd handle to the spooler.

In WlsWritePrinter(), I chop the spooler’s data block into smaller frames by the size of __WlsFrmSize. Then I send each frame by calling __WlsTransferFrame() where I add some control bytes to form a packet to be delivered by the underlying driver function. Another function WlsGetPrinterData() gets called in the initialization of the port configuration dialog in dialogs.c. In WlsGetPrinterData(), you must write __WlsGetFirstPrinter()/__WlsGetNextPrinter() to obtain the wireless printer names from the current network.

However, in the code archive, wlsfnc.c is not supplied as that in Listing 3. For simulation purposes, I demonstrate the sample WLSMON.DLL without concrete wireless code; i.e., without real device-driver support. But for an application, you still can use File|Print to simulate printing via WLS ports in Figure 1. I show the print job ID in a message box in WlsConnectPrinter() and show the total bytes received in WlsDisconnectPrinter(). This simulation will still give the look and feel of the WLSMON monitor and let you manipulate its print interface from the spooler.

Your Own Wireless Implementation

In this article, I expose the architecture of a wireless port monitor from outer to inner with a top-down approach. I provide a clearer picture of how the WLSMON monitor works between the spooler and wireless printers in Windows NT and Windows 2000 systems. I separate the wireless code in an independent file containing function placeholders that a developer knows how to couple his own wireless implementations. Also I hope this article can answer most questions from the readers who are interested in the WLSMON and MonSet developments.

Note that in testing, when you change a port for the default printer (e.g., from LPT1 to WLS1), you might encounter a problem in which the port remains the same. Regarding this problem of registry updating, see my previous Tech Tips in the August 1999 and December 1998 issues of Windows Developer’s Journal. I found this bug has not been fixed in Windows 2000 at the time of this writing.

References

Zuoliu Ding. “A Print Monitor Setup Utility,” Windows Developer’s Journal, January 2000.

The localmon.dll sample source code, see DDK\src\Print\LocalMon.

Print monitor overview, see msdn.microsoft.com/library/en-us/graphics/provider_6cyv.asp or in the MSDN CD, mk:@ivt:ntddk/native/ddk/gg/src/prmon.htm.

Wireless Application Protocol (WAP), see www.wapforum.org.

IEEE 802 standards, see standards.ieee.org/getieee802/.

The Wireless LAN Association, see www.wlana.com.

Zuoliu Ding. Tech Tip: “Changing Printer Ports in the Registry,” Windows Developer’s Journal, August 1999.

Zuoliu Ding. WDJ Tech Tip “Changing the Default Printer Port on NT 4.0,” Windows Developer’s Journal, December 1998.

About the Author

Zuoliu Ding works at Comarco Wireless Technologies in Irvine, CA, with field measurement software for communication networks. He can be reached at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.