Implementing Wireless Print for WinNT/Win2K

WLSMON is a wireless print monitor prototype. By modifying a few function placeholders, you can customize it for your own wireless implementation.


November 01, 2001
URL:http://www.drdobbs.com/implementing-wireless-print-for-winntwin/184416369

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].

November 2001/Implementing Wireless Print for WinNT/Win2K

Figure 1: Printing via a wireless port

November 2001/Implementing Wireless Print for WinNT/Win2K

Figure 2: Each PC node and printer node has its own transceiver and is assigned a number or a string as a unique ID. Multiple PC nodes can communicate with multiple printer nodes

November 2001/Implementing Wireless Print for WinNT/Win2K

Figure 3: Dialog for selecting which printers to target

November 2001/Implementing Wireless Print for WinNT/Win2K

Figure 4: Adding “WLS3:” as an available wireless port

November 2001/Implementing Wireless Print for WinNT/Win2K

Figure 5: The newly added port is configured using WLSMON’s ConfigurePort()

November 2001/Implementing Wireless Print for WinNT/Win2K

Figure 6: The registry settings for the available WLS ports are stored in the subkey Ports

November 2001/Implementing Wireless Print for WinNT/Win2K

Listing 1: wlsmon.c
The implementation of the WLSMON API

/*------------------------------------------------------------------
WlsMon.c: Implementation of Wireless Print Monitor 
Author:   Zuoliu Ding
------------------------------------------------------------------*/
#include <windows.h>
#include <winspool.h>
#include <winsplp.h>
#include <wchar.h>
#include <regstr.h>
#include "wlsmon.h"
 
#define WLS_REG REGSTR_PATH_MONITORS L"\\Wireless Port\\Ports"
#define WLS L"WLS"

HANDLE   hInst;
PINIPORT pIniFirstPort;
CRITICAL_SECTION SpoolerSection;     

static HKEY hHKLM = HKEY_LOCAL_MACHINE; 
static HKEY hKey; 
static LONG lRet; 
    
BOOL DllEntryPoint(HANDLE hModule, DWORD dwReason, LPVOID lpRes)
{
    switch (dwReason)
    {
        case DLL_PROCESS_ATTACH:   
        {
            hInst = hModule;
            InitializeCriticalSection(&SpoolerSection);
            DisableThreadLibraryCalls(hModule);
            return TRUE;
        }
        case DLL_PROCESS_DETACH: 
            return TRUE;
    }
    UNREFERENCED_PARAMETER( lpRes );
}

MONITOREX MonitorEx = {
    sizeof(MONITOR),
    {
        EnumPorts,
        OpenPort,
        NULL,           // OpenPortEx is not supported
        StartDocPort,
        WritePort,
        ReadPort,
        EndDocPort,
        ClosePort,
        AddPort,
        NULL,           // AddPortEx is not supported
        ConfigurePort,
        DeletePort,
        NULL,           // GetPrinterDataFromPort is not supported
        SetPortTimeOuts
    }
};

LPMONITOREX InitializePrintMonitor(LPWSTR pRegistryRoot)
{ 
    DWORD  dwLen, dwIdx=0; 
    WCHAR  wsz[80];     
    DWORD  dwDisposition; 

    EnterSplSem();
    lRet = RegCreateKeyEx(hHKLM, WLS_REG, 0, NULL, 
                          REG_OPTION_NON_VOLATILE, 
                          KEY_READ, NULL, &hKey, &dwDisposition);
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"RCP:RegCreateKeyEx")) 
        return FALSE;
  
    while (TRUE)
    {
        dwLen =80;
        lRet = RegEnumValue(hKey, dwIdx, wsz, &dwLen, 
                            NULL, NULL, NULL, NULL);
        if (lRet==ERROR_NO_MORE_ITEMS || lRet!=ERROR_SUCCESS) break;

        dwIdx++;  
        CreatePortEntry(wsz);
    }              

    lRet = RegCloseKey(hKey);   // handle of key to close  
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"IPM:RegCloseKey")) 
        return FALSE;

    LeaveSplSem();
    return &MonitorEx;
}

BOOL StartDocPort(HANDLE hPort, LPWSTR pPrinterName, DWORD JobId, 
                  DWORD Level,  LPBYTE pDocInfo)
{
    PINIPORT    pIniPort = (PINIPORT)hPort;
    if (pIniPort->Status & PP_STARTDOC) return TRUE;  
    
    pIniPort->pPrinterName = AllocSplStr(pPrinterName);
    if (IsError(NULL, !pIniPort->pPrinterName, L"SDP:No Printer")) 
        return FALSE;
 
    if (IsError(NULL, 
                !IsWLSPort(pIniPort->pName), L"SDP:Not WLSPort")) 
        return FALSE;

    pIniPort->hPrinter = WlsConnectPrinter(pIniPort->pName, JobId);
    if (IsError(NULL, !pIniPort->hPrinter, L"SDP:No Printer Hnd")) 
        return FALSE;

    pIniPort->JobId = JobId;
    pIniPort->Status |= PP_STARTDOC;
    return TRUE;
}

BOOL WritePort(HANDLE hPort, LPBYTE pBuffer, 
                             DWORD cbBuf, LPDWORD pcbWritten)
{
    PINIPORT pIniPort = (PINIPORT)hPort;

    if (IsError(NULL, !pIniPort->hPrinter, L"WP:No Printer Handle")) 
        return FALSE;
     
    lRet = (LONG)WlsWritePrinter(pIniPort->hPrinter, 
                           pBuffer, cbBuf, pcbWritten);
    if (!lRet)  EndDocPort(hPort);
    
    return (BOOL)lRet;
}
 
BOOL EndDocPort(HANDLE hPort)
{
    PINIPORT pIniPort = (PINIPORT)hPort;
    HANDLE   hPrinter;

    if (!(pIniPort->Status & PP_STARTDOC)) return TRUE;  

    if (IsError(NULL, !pIniPort->hPrinter, L"EDP:No Printer Hnd")) 
        return FALSE;

    WlsDisconnectPrinter(pIniPort->hPrinter);
   
    if (OpenPrinter(pIniPort->pPrinterName, &hPrinter, NULL)) 
    {
        if (hPrinter) 
        {  
            SetJob(hPrinter, pIniPort->JobId, 
                    0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
            ClosePrinter(hPrinter);
        }                                
    }
   
    FreeSplStr(pIniPort->pPrinterName);
    pIniPort->Status &= ~PP_STARTDOC;
    return TRUE;
}

BOOL OpenPort(LPWSTR pName,  PHANDLE pHandle)
{
    EnterSplSem();
    *pHandle = FindIniKey((PINIENTRY)pIniFirstPort, pName);
    LeaveSplSem();
    return TRUE;
}

BOOL ClosePort(HANDLE  hPort) 
{ 
    PINIPORT pIniPort = (PINIPORT)hPort;
    if (!pIniPort) return FALSE;

    EnterSplSem();
    DeletePortNode(pIniPort);
    LeaveSplSem();
    return TRUE;
}

BOOL ReadPort(HANDLE, LPBYTE pBuffer, DWORD cbBuf, LPDWORD pcbRead)
{
    return TRUE;
}

BOOL SetPortTimeOuts(HANDLE, LPCOMMTIMEOUTS lpCTO, DWORD reserved)
{
    return FALSE;
}

BOOL RegWritePort(LPWSTR  pPortName, LPWSTR pVal)
{ 
    WCHAR wsz[80];     
    lRet = RegOpenKeyEx(hHKLM, WLS_REG, 0, KEY_WRITE, &hKey);
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"RWP:RegOpenKeyEx")) 
        return FALSE;
  
    lRet = RegSetValueEx(hKey, pPortName, 0, REG_SZ, 
                         (LPBYTE)pVal, sizeof(WCHAR)*wcslen(pVal));
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"RWP:RegSetValueEx")) 
        return FALSE;

    lRet = RegCloseKey(hKey);    
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"RWP:RegCloseKey")) 
        return FALSE;
  
    return TRUE;
}

BOOL RegReadPort(LPWSTR  pPortName, LPWSTR pVal)
{ 
    DWORD dwLen=80, dwType=REG_SZ; 
    lRet = RegOpenKeyEx(hHKLM, WLS_REG, 0, KEY_READ, &hKey);
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"RRP:RegOpenKeyEx")) 
        return FALSE;
          
    lRet =RegQueryValueEx(hKey, pPortName, 0, &dwType, 
                          (LPBYTE)pVal, &dwLen);
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"RRP:RegQueryValueEx")) 
        return FALSE;

    lRet = RegCloseKey(hKey);  
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"RRP:RegCloseKey")) 
        return FALSE;

    return TRUE;
}

BOOL RegDeletePort(LPWSTR  pPortName)
{ 
    lRet = RegOpenKeyEx(hHKLM, WLS_REG, 0, KEY_WRITE, &hKey);
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"RDP:RegOpenKeyEx")) 
        return FALSE;
  
    lRet = RegDeleteValue(hKey, pPortName);
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"RDP:RegDeleteValue")) 
        return FALSE;

    lRet = RegCloseKey(hKey);   
    if (IsError(NULL, lRet!=ERROR_SUCCESS, L"RDP:RegCloseKey")) 
        return FALSE;
    
    return TRUE;
}

WORD RegGetPortData(LPWSTR  pszVal)
{   
    LPWSTR pTmo = wcsstr(pszVal, L"::");
    if (!pTmo) return 0;

    *pTmo++ =0; 
    *pTmo++ =0;
    return (WORD)wcstoul(pTmo, NULL, 10);
}

BOOL IsWLSPort(LPWSTR pPort)
{
    if (_wcsnicmp(pPort, WLS, 3)) return FALSE;
    return pPort[wcslen(pPort) -1] == L':';  
}

BOOL IsError(HWND hWnd, BOOL bErr, LPWSTR wsMsg)
{            
    if (bErr) MessageBox(hWnd, wsMsg, L"WLS Monitor Error", MB_OK); 
    return bErr;
}

/* End of File */
November 2001/Implementing Wireless Print for WinNT/Win2K

Listing 2: config.c
Contains four port-management functions: adding, deleting, configuring, and enumerating

/*------------------------------------------------------------------
config.c(part): Adding, deleting, configuring, and enumerating ports
Author:         Zuoliu Ding
------------------------------------------------------------------*/

BOOL AddPort(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
{
    LPWSTR pPortName;
    DWORD  ThreadId, WindowThreadId, Error; 
    BOOL   rc = TRUE, DoAddPort = TRUE;
    WCHAR  szMonitor[MAX_PATH+1];

    LoadString(hInst, IDS_WLSMONITORPORT, szMonitor, MAX_PATH);
    if (_wcsicmp(pMonitorName, szMonitor)) { 
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;                    // not a Wireless Port
    }

    ThreadId = GetCurrentThreadId( );
    WindowThreadId = GetWindowThreadProcessId(hWnd, NULL);

    if (AttachThreadInput(ThreadId, WindowThreadId, TRUE))
    {
        pPortName = GetPortName(hWnd); // Get a port name from user
        if (pPortName)
        {
            if (PortExists(pName, pPortName, &Error))
            {
                Message(hWnd, MSG_ERROR, IDS_WLSMONITORPORT,
                        IDS_PORTALREADYEXISTS_S, pPortName );
                DoAddPort = FALSE; // error was handled, return true
            }
            else if (Error != NO_ERROR)
            {
                DoAddPort = FALSE;
                rc = FALSE;
            }

            if (DoAddPort && IsWLSPort(pPortName))
            {
                CharUpperBuff(pPortName, 3);
                if (RegWritePort(pPortName, L"::120")) 
                {   
                    if (!ConfigurePort(NULL, hWnd, pPortName) ||  
                        !CreatePortEntry(pPortName))   
                    {     
                        RegDeletePort(pPortName);
                        rc = FALSE; 
                    } 

                }
            }
        }  // if( pPortName )

        AttachThreadInput(WindowThreadId, ThreadId, FALSE);
    }

    return rc;
}

BOOL DeletePort(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
{
    BOOL rc;
    EnterSplSem();
    if (rc=DeletePortEntry(pPortName)) RegDeletePort(pPortName);
    LeaveSplSem();

    return rc;
    UNREFERENCED_PARAMETER(pName);
}

BOOL ConfigurePort(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
{
    DWORD  ThreadId, WindowThreadId;
    ThreadId = GetCurrentThreadId( );
    WindowThreadId = GetWindowThreadProcessId(hWnd, NULL);

    if (AttachThreadInput(ThreadId, WindowThreadId, TRUE))
    {
        if(IsWLSPort( pPortName ))
        {
           DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_CONFIGURE_WCP),
                          hWnd,    (DLGPROC)ConfigurePortDlg, 
                          (LPARAM)&pPortName); 
        }
        else Message(hWnd, MSG_ERROR, IDS_WLSMONITORPORT, 
                     IDS_NOTHING_TO_CONFIGURE);

        AttachThreadInput(WindowThreadId, ThreadId, FALSE);
    }                                                            
    return TRUE;
}

BOOL EnumPorts(LPWSTR pName, DWORD Level, LPBYTE pPorts, 
               DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
{
    PINIPORT pIniPort;
    DWORD    cb=0, LastError=0;
    LPBYTE   pEnd;

    EnterSplSem();
    pIniPort=pIniFirstPort;

    while (pIniPort) {
        cb+=GetPortSize(pIniPort, Level);
        pIniPort=pIniPort->pNext;
    }

    *pcbNeeded=cb;

    if (cb <= cbBuf) 
    {
        pEnd=pPorts+cbBuf;
        *pcReturned=0;
        pIniPort=pIniFirstPort;

        while (pIniPort) 
        {
            pEnd = CopyIniPortToPort(pIniPort, Level, pPorts, pEnd);

            switch (Level) 
            {
                case 1: pPorts+=sizeof(PORT_INFO_1);
                        break;
                case 2: pPorts+=sizeof(PORT_INFO_2);
                        break;
                default: LastError = ERROR_INVALID_LEVEL;
                         goto Cleanup;
            }

            (*pcReturned)++;
            pIniPort=pIniPort->pNext;
        }

    } 
    else LastError = ERROR_INSUFFICIENT_BUFFER;

Cleanup:
    LeaveSplSem();

    if (LastError) {
        SetLastError(LastError);
        return FALSE;
    } 
    else return TRUE;
}
/* End of File */
November 2001/Implementing Wireless Print for WinNT/Win2K

Listing 3: wlsfnc.c
Source for wireless procedures. The functions prefixed with two underscores need further implementation based on technology

/*------------------------------------------------------------------
wlsfnc.c: Implement wireless communications, for WDJ article only
Note:     __WlsXxx functions need technology-based implementations
Author:   Zuoliu Ding
------------------------------------------------------------------*/
#include <windows.h>
#include "wlsmon.h"

#define __WlsFrmSize 240

typedef struct tag__WLS_HANDLE 
{  
    LPWSTR  pNetwork;
    LPWSTR  pNode1;
    LPWSTR  pNode2;

    WORD    wTmo;
    DWORD   dwStatus;

    // Other members...
} *__WLS_HANDLE;


HANDLE WlsConnectPrinter(LPWSTR pName, DWORD JobId)
{ 
    WCHAR wcs[256], wcs2[256];        
    __WLS_HANDLE wlsHnd;

    // Here do some sanitary check for network first...

    if (!(wlsHnd =__WlsOpenHandle()) // Allocate memory for handle 
        return NULL;

    RegReadPort(pName, wcs);        // Read settings from registry
    WORD wTmo =RegGetPortData(wcs); // Get prn node name and timeout
    
    __WlsGetLocalUserName(wsz2);    // Get PC node name
    __WlsSetNodeNames(wlsHnd, wsz2, wcs);
    wlsHnd->wTmo = wTmo;

    // Here set other data members before connection...
    if (!__WlsSetConnection(wlsHnd))  
    {
        // Here do cleanup...
        return NULL;
    }

    return  (HANDLE)wlsHnd;  
}                     

BOOL WlsWritePrinter(HANDLE hPrinter, 
                     LPBYTE pBuffer, DWORD cbBuf, LPDWORD pcbWrtn)
{    
    __WLS_HANDLE wlsHnd = (__WLS_HANDLE)hPrinter;

    DWORD    dwCurLen = cbBuf; 
    DWORD    dwFrmLen; 
    *pcbWrtn =0;
    
    // Here validate network and wlsHnd first...

    while (dwCurLen>0)
    {         
        dwFrmLen = dwCurLen< __WlsFrmSize ? dwCurLen: __WlsFrmSize; 
             
        if (!__WlsTransferFrame(wlsHnd, pBuffer, dwFrmLen)) 
        {
            // Here do cleanup...
            return FALSE;
        }
     
        dwCurLen     -= dwFrmLen;
        pBuffer         += dwFrmLen; 
        *pcbWrtn    += dwFrmLen;
    }                              
  
    return TRUE;
}

BOOL WlsDisconnectPrinter(HANDLE hPrinter)  
{
    __WLS_HANDLE wlsHnd = (__WLS_HANDLE)hPrinter;
    // Here validate wlsHnd and do some other cleanup...
    
    if (!__WlsTerminateConnection(wlsHnd)) return FALSE; 

    __WlsCloseHandle(wlsHnd);
    return TRUE;
}

void WlsGetPrinterData(HWND hwnd) 
{
    WCHAR  wsz[256]; 
    int i;

    __WlsGetNetworkName(wsz);
    SetDlgItemText(hwnd, IDD_CW_EDTNETWORK, wsz); 

    __WlsGetLocalUserName(wsz);
    SetDlgItemText(hwnd, IDD_CW_LOCALUNIT, wsz); 

    SendDlgItemMessage(hwnd, IDD_CW_LISTPRNS, LB_RESETCONTENT, 0,0);

    if (!__WlsGetFirstPrinter(wsz)) return;

    do     
    {   
        SendDlgItemMessage(hwnd, IDD_CW_LISTPRNS, 
                                 LB_ADDSTRING, 0, (LPARAM)wsz);
    }
    while (__WlsGetNextPrinter(wsz));
}

//End of file

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.