2020-09-30 17:12:29 +02:00

1192 lines
26 KiB
C

/*++
Copyright (c) 1990-1994 Microsoft Corporation
All rights reserved
Module Name:
SplInit.c
Abstract:
Initialize the spooler.
Author:
Environment:
User Mode -Win32
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
LPWSTR szDevice = L"Device";
LPWSTR szPrinters = L"Printers";
LPWSTR szDeviceOld = L"DeviceOld";
LPWSTR szNULL = L"";
LPWSTR szPorts=L"Ports";
LPWSTR szWinspool = L"winspool";
LPWSTR szNetwork = L"Ne";
LPWSTR szTimeouts = L",15,45";
LPWSTR szComma = L",";
LPWSTR szDotDefault = L".Default";
LPWSTR szRegDevicesPath = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
LPWSTR szRegWindowsPath = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
LPWSTR szRegPrinterPortsPath = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\PrinterPorts";
LPWSTR szCurrentVersionPath = L"Software\\Microsoft\\Windows NT\\CurrentVersion";
typedef struct INIT_REG_USER {
HKEY hKeyUser;
HKEY hKeyWindows;
HKEY hKeyDevices;
HKEY hKeyPrinterPorts;
BOOL bFoundPrinter;
BOOL bDefaultSearch;
BOOL bDefaultFound;
BOOL bFirstPrinterFound;
DWORD dwNetCounter;
WCHAR szFirstPrinter[MAX_PATH * 2];
WCHAR szDefaultPrinter[MAX_PATH * 2];
} INIT_REG_USER, *PINIT_REG_USER;
//
// Prototypes
//
BOOL
InitializeRegUser(
LPWSTR szSubKey,
PINIT_REG_USER pUser
);
VOID
FreeRegUser(
PINIT_REG_USER pUser
);
BOOL
SetupRegForUsers(
PINIT_REG_USER pUsers,
DWORD cUsers
);
VOID
UpdateUsersDefaultPrinter(
PINIT_REG_USER pUser
);
DWORD
ReadPrinters(
PINIT_REG_USER pUser,
DWORD Flags,
PDWORD pcbPrinters,
LPBYTE* ppPrinters
);
BOOL
UpdatePrinterInfo(
const PINIT_REG_USER pCurUser,
LPCWSTR pPrinterName,
LPCWSTR pPorts,
PDWORD pdwNetId
);
BOOL
EnumerateConnectedPrinters(
LPBYTE pPrinter,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned,
HKEY hKeyUser
);
VOID
RegClearKey(
HKEY hKey
);
LPWSTR
CheckBadPortName(
LPWSTR pszPort
);
BOOL
SpoolerInitAll(
VOID
)
{
DWORD dwError;
WCHAR szClass[MAX_PATH];
WCHAR szSubKey[MAX_PATH];
DWORD cUsers;
DWORD cSubKeys;
DWORD cchMaxSubkey;
DWORD cchMaxClass;
DWORD cValues;
DWORD cbMaxValueData;
DWORD cbSecurityDescriptor;
DWORD cchClass;
DWORD cchMaxValueName;
FILETIME ftLastWriteTime;
BOOL bSuccess;
DWORD cchSubKey;
PINIT_REG_USER pUsers;
PINIT_REG_USER pCurUser;
DWORD i;
cchClass = COUNTOF(szClass);
dwError = RegQueryInfoKey(HKEY_USERS,
szClass,
&cchClass,
NULL,
&cSubKeys,
&cchMaxSubkey,
&cchMaxClass,
&cValues,
&cchMaxValueName,
&cbMaxValueData,
&cbSecurityDescriptor,
&ftLastWriteTime);
if (dwError) {
SetLastError( dwError );
DBGMSG(DBG_WARNING, ("SpoolerIniAll failed RegQueryInfoKey HKEY_USERS error %d\n", dwError));
return FALSE;
}
if (cSubKeys < 1)
return TRUE;
pUsers = AllocSplMem(cSubKeys * sizeof(pUsers[0]));
if (!pUsers) {
DBGMSG(DBG_WARNING, ("SpoolerIniAll failed to allocate pUsers error %d\n", dwError));
return FALSE;
}
for (i=0, pCurUser=pUsers, cUsers=0;
i< cSubKeys;
i++) {
cchSubKey = COUNTOF(szSubKey);
dwError = RegEnumKeyEx(HKEY_USERS,
i,
szSubKey,
&cchSubKey,
NULL,
NULL,
NULL,
&ftLastWriteTime);
if ( dwError ) {
// BUGBUG
// Should return error here if we fail to initialize a user
DBGMSG( DBG_WARNING, ("SpoolerInitAll failed RegEnumKeyEx HKEY_USERS %ws %d %d\n", szSubKey, i, dwError));
SetLastError( dwError );
} else {
if (!_wcsicmp(szSubKey, szDotDefault)) {
continue;
}
if (InitializeRegUser(szSubKey, pCurUser)) {
pCurUser++;
cUsers++;
}
}
}
bSuccess = SetupRegForUsers(pUsers,
cUsers);
for (i=0; i< cUsers; i++)
FreeRegUser(&pUsers[i]);
//
// In case we are starting after the user has logged in, inform
// all applications that there may be printers now.
//
BroadcastMessage(BROADCAST_TYPE_CHANGEDEFAULT,
0,
0,
0);
FreeSplMem(pUsers);
if ( !bSuccess ) {
DBGMSG( DBG_WARNING, ("SpoolerInitAll failed error %d\n", GetLastError() ));
} else {
DBGMSG( DBG_TRACE, ("SpoolerInitAll Success\n" ));
}
return bSuccess;
}
BOOL
SetupRegForUsers(
PINIT_REG_USER pUsers,
DWORD cUsers)
{
DWORD cbPrinters;
DWORD cPrinters;
PBYTE pPrinters;
#define pPrinters2 ((PPRINTER_INFO_2)pPrinters)
#define pPrinters4 ((PPRINTER_INFO_4)pPrinters)
DWORD i, j;
LPWSTR pszPort;
//
// Read in local printers.
//
cbPrinters = 1000;
pPrinters = AllocSplMem(cbPrinters);
if (!pPrinters)
return FALSE;
if (cPrinters = ReadPrinters(NULL,
PRINTER_ENUM_LOCAL,
&cbPrinters,
&pPrinters)) {
for (i=0; i< cUsers; i++) {
for(j=0; j< cPrinters; j++) {
if( pPrinters2[j].Attributes & PRINTER_ATTRIBUTE_NETWORK ){
//
// Use NeXX:
//
pszPort = NULL;
} else {
pszPort = CheckBadPortName( pPrinters2[j].pPortName );
}
UpdatePrinterInfo( &pUsers[i],
pPrinters2[j].pPrinterName,
pszPort,
&(pUsers[i].dwNetCounter));
}
}
}
for (i=0; i< cUsers; i++) {
if (cPrinters = ReadPrinters(&pUsers[i],
PRINTER_ENUM_CONNECTIONS,
&cbPrinters,
&pPrinters)) {
for(j=0; j< cPrinters; j++) {
UpdatePrinterInfo(&pUsers[i],
pPrinters4[j].pPrinterName,
NULL,
&(pUsers[i].dwNetCounter));
}
}
}
FreeSplMem(pPrinters);
for (i=0; i< cUsers; i++) {
UpdateUsersDefaultPrinter(&pUsers[i]);
}
return TRUE;
#undef pPrinters2
#undef pPrinters4
}
VOID
UpdateUsersDefaultPrinter(
PINIT_REG_USER pUser)
{
LPWSTR pszNewDefault = NULL;
//
// If default wasn't present, and we did get a first printer,
// make this the default.
//
if (!pUser->bDefaultFound) {
if (pUser->bFirstPrinterFound) {
pszNewDefault = pUser->szFirstPrinter;
}
} else {
//
// Write out default.
//
pszNewDefault = pUser->szDefaultPrinter;
}
if (pszNewDefault) {
RegSetValueEx(pUser->hKeyWindows,
szDevice,
0,
REG_SZ,
(PBYTE)pszNewDefault,
(wcslen(pszNewDefault) + 1) * sizeof(pszNewDefault[0]));
}
}
DWORD
ReadPrinters(
PINIT_REG_USER pUser,
DWORD Flags,
PDWORD pcbPrinters,
LPBYTE* ppPrinters)
{
BOOL bSuccess;
DWORD cbNeeded;
DWORD cPrinters = 0;
if (Flags == PRINTER_ENUM_CONNECTIONS) {
bSuccess = EnumerateConnectedPrinters(*ppPrinters,
*pcbPrinters,
&cbNeeded,
&cPrinters,
pUser->hKeyUser);
} else {
bSuccess = EnumPrinters(Flags,
NULL,
2,
(PBYTE)*ppPrinters,
*pcbPrinters,
&cbNeeded,
&cPrinters);
}
if (!bSuccess) {
//
// If not enough space, realloc.
//
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if (*ppPrinters = ReallocSplMem(*ppPrinters,
0,
cbNeeded)) {
*pcbPrinters = cbNeeded;
}
}
if (Flags == PRINTER_ENUM_CONNECTIONS) {
bSuccess = EnumerateConnectedPrinters(*ppPrinters,
*pcbPrinters,
&cbNeeded,
&cPrinters,
pUser->hKeyUser);
} else {
bSuccess = EnumPrinters(Flags,
NULL,
2,
(PBYTE)*ppPrinters,
*pcbPrinters,
&cbNeeded,
&cPrinters);
}
if (!bSuccess)
cPrinters = 0;
}
return cPrinters;
}
BOOL
UpdatePrinterInfo(
const PINIT_REG_USER pCurUser,
LPCWSTR pszPrinterName,
LPCWSTR pszPort,
PDWORD pdwNetId
)
/*++
Routine Description:
Updates the printer information in the registry win.ini.
Arguments:
pCurUser - Information about the user. The following fields are
used by this routine:
hKeyDevices
hKeyPrinterPorts
bDefaultSearch (if true, read/writes to:)
bDefaultFound
szDefaultPrinter
bFirstPrinterFound (if false, writes to:)
szFirstPrinter
pszPort - Port name. If NULL, generates NetId.
pdwNetId - Pointer to NetId counter. This value will be incremented
if the NetId is used.
Return Value:
--*/
{
WCHAR szBuffer[MAX_PATH * 2];
LPWSTR p;
DWORD dwCount = 0;
DWORD cbLen;
if (!pszPrinterName)
return FALSE;
//
// Now we know the spooler is up, since the EnumPrinters succeeded.
// Update all sections.
//
dwCount = wsprintf(szBuffer,
L"%s,",
szWinspool);
if( !pszPort ){
HANDLE hToken;
wsprintf(&szBuffer[dwCount],
L"%s%.2d:",
szNetwork,
*pdwNetId);
(*pdwNetId)++;
//
// !! HACK !!
//
// Works 3.0b expects the printer port entry in the
// [ports] section.
//
// This is in the per-machine part of the registry, but we
// are updating it for each user. Fix later.
//
// We never remove the NeXX: entries from [ports] but since
// the same entries will be used by all users, this is ok.
//
hToken = RevertToPrinterSelf();
WriteProfileString( szPorts, &szBuffer[dwCount], L"" );
if( hToken ){
ImpersonatePrinterClient( hToken );
}
//
// End Works 3.0b HACK
//
} else {
UINT cchBuffer;
cchBuffer = wcslen( szBuffer );
wcscpy(&szBuffer[cchBuffer], pszPort);
//
// Get the first port only.
//
wcstok(&szBuffer[cchBuffer], szComma);
}
cbLen = (wcslen(szBuffer)+1) * sizeof(szBuffer[0]);
RegSetValueEx(pCurUser->hKeyDevices,
pszPrinterName,
0,
REG_SZ,
(PBYTE)szBuffer,
cbLen);
//
// If the user has a default printer specified, then verify
// that it exists.
//
if (pCurUser->bDefaultSearch) {
pCurUser->bDefaultFound = !_wcsicmp(pszPrinterName,
pCurUser->szDefaultPrinter);
if (pCurUser->bDefaultFound) {
wsprintf(pCurUser->szDefaultPrinter,
L"%s,%s",
pszPrinterName,
szBuffer);
pCurUser->bDefaultSearch = FALSE;
}
}
if (!pCurUser->bFirstPrinterFound) {
wsprintf(pCurUser->szFirstPrinter,
L"%s,%s",
pszPrinterName,
szBuffer);
pCurUser->bFirstPrinterFound = TRUE;
}
wcscat(szBuffer, szTimeouts);
RegSetValueEx(pCurUser->hKeyPrinterPorts,
pszPrinterName,
0,
REG_SZ,
(PBYTE)szBuffer,
(wcslen(szBuffer)+1) * sizeof(szBuffer[0]));
return TRUE;
}
BOOL
SpoolerInit(
VOID
)
/*++
Routine Description:
Initializes just the current user.
Arguments:
Return Value:
--*/
{
INIT_REG_USER User;
BOOL bSuccess = FALSE;
//
// Enum just the current user.
//
User.hKeyUser = GetClientUserHandle(KEY_READ|KEY_WRITE);
if (User.hKeyUser) {
if (InitializeRegUser(NULL, &User)) {
//
// setup user
//
bSuccess = SetupRegForUsers(&User, 1);
//
// This will close User.hKey.
//
FreeRegUser(&User);
}
}
return bSuccess;
}
BOOL
InitializeRegUser(
LPWSTR pszSubKey,
PINIT_REG_USER pUser
)
/*++
Routine Description:
Initialize a single users structure based on a HKEY_USERS subkey.
Arguments:
pszSubKey - if non-NULL initialize hKeyUser to this key
pUser - structure to initialize
Return Value:
--*/
{
HKEY hKey;
DWORD cbData;
DWORD dwDisposition;
PSECURITY_DESCRIPTOR pSD = NULL;
DWORD cbSD = 0;
DWORD dwError;
BOOL bSecurityLoaded = FALSE;
BOOL rc = FALSE;
HANDLE hToken = NULL;
if (pszSubKey) {
if (RegOpenKeyEx(HKEY_USERS,
pszSubKey,
0,
KEY_READ|KEY_WRITE,
&pUser->hKeyUser) != ERROR_SUCCESS) {
DBGMSG(DBG_WARNING, ("InitializeRegUser: RegOpenKeyEx failed\n"));
goto Fail;
}
}
//
// Now attempt to set the security on these two keys to
// their parent key.
//
dwError = RegOpenKeyEx(pUser->hKeyUser,
szCurrentVersionPath,
0,
KEY_READ,
&hKey);
if (!dwError) {
dwError = RegGetKeySecurity(hKey,
DACL_SECURITY_INFORMATION,
pSD,
&cbSD);
if (dwError == ERROR_INSUFFICIENT_BUFFER) {
pSD = AllocSplMem(cbSD);
if (pSD) {
if (!RegGetKeySecurity(hKey,
DACL_SECURITY_INFORMATION,
pSD,
&cbSD)){
bSecurityLoaded = TRUE;
} else {
DBGMSG(DBG_WARNING, ("InitializeRegUser: RegGetKeySecurity failed %d\n",
GetLastError()));
}
}
} else {
DBGMSG(DBG_WARNING, ("InitializeRegUser: RegGetKeySecurity failed %d\n",
dwError));
}
RegCloseKey(hKey);
} else {
DBGMSG(DBG_WARNING, ("InitializeRegUser: RegOpenKeyEx CurrentVersion failed %d\n",
dwError));
}
hToken = RevertToPrinterSelf();
//
// Open up the right keys.
//
if (RegCreateKeyEx(pUser->hKeyUser,
szRegDevicesPath,
0,
szNULL,
0,
KEY_ALL_ACCESS,
NULL,
&pUser->hKeyDevices,
&dwDisposition) != ERROR_SUCCESS) {
DBGMSG(DBG_WARNING, ("InitializeRegUser: RegCreateKeyEx1 failed %d\n",
GetLastError()));
goto Fail;
}
if (bSecurityLoaded) {
RegSetKeySecurity(pUser->hKeyDevices,
DACL_SECURITY_INFORMATION,
pSD);
}
if (RegCreateKeyEx(pUser->hKeyUser,
szRegPrinterPortsPath,
0,
szNULL,
0,
KEY_ALL_ACCESS,
NULL,
&pUser->hKeyPrinterPorts,
&dwDisposition) != ERROR_SUCCESS) {
DBGMSG(DBG_WARNING, ("InitializeRegUser: RegCreateKeyEx2 failed %d\n",
GetLastError()));
goto Fail;
}
if (bSecurityLoaded) {
RegSetKeySecurity(pUser->hKeyPrinterPorts,
DACL_SECURITY_INFORMATION,
pSD);
}
//
// First, attempt to clear out the keys by deleting them.
//
RegClearKey(pUser->hKeyDevices);
RegClearKey(pUser->hKeyPrinterPorts);
if (RegOpenKeyEx(pUser->hKeyUser,
szRegWindowsPath,
0,
KEY_READ|KEY_WRITE,
&pUser->hKeyWindows) != ERROR_SUCCESS) {
DBGMSG(DBG_WARNING, ("InitializeRegUser: RegOpenKeyEx failed %d\n",
GetLastError()));
goto Fail;
}
pUser->bFoundPrinter = FALSE;
pUser->bDefaultSearch = FALSE;
pUser->bDefaultFound = FALSE;
pUser->bFirstPrinterFound = FALSE;
pUser->dwNetCounter = 0;
cbData = sizeof(pUser->szDefaultPrinter);
if (RegQueryValueEx(pUser->hKeyWindows,
szDevice,
NULL,
NULL,
(PBYTE)pUser->szDefaultPrinter,
&cbData) == ERROR_SUCCESS) {
pUser->bDefaultSearch = TRUE;
}
//
// Remove the Device= in [windows]
//
RegDeleteValue(pUser->hKeyWindows,
szDevice);
if (!pUser->bDefaultSearch) {
//
// Attempt to read from saved location.
//
if (RegOpenKeyEx(pUser->hKeyUser,
szPrinters,
0,
KEY_READ,
&hKey) == ERROR_SUCCESS) {
cbData = sizeof(pUser->szDefaultPrinter);
//
// Try reading szDeviceOld.
//
if (RegQueryValueEx(
hKey,
szDeviceOld,
NULL,
NULL,
(PBYTE)pUser->szDefaultPrinter,
&cbData) == ERROR_SUCCESS) {
pUser->bDefaultSearch = TRUE;
}
RegCloseKey(hKey);
}
}
if (pUser->bDefaultSearch) {
wcstok(pUser->szDefaultPrinter, szComma);
}
rc = TRUE;
Fail:
if (hToken) {
ImpersonatePrinterClient(hToken);
}
if (pSD) {
FreeSplMem(pSD);
}
if (!rc)
FreeRegUser(pUser);
return rc;
}
VOID
FreeRegUser(
PINIT_REG_USER pUser)
/*++
Routine Description:
Free up the INIT_REG_USER structure intialized by InitializeRegUser.
Arguments:
Return Value:
--*/
{
if (pUser->hKeyUser) {
CloseHandle(pUser->hKeyUser);
pUser->hKeyUser = NULL;
}
if (pUser->hKeyDevices) {
CloseHandle(pUser->hKeyDevices);
pUser->hKeyDevices = NULL;
}
if (pUser->hKeyPrinterPorts) {
CloseHandle(pUser->hKeyPrinterPorts);
pUser->hKeyPrinterPorts = NULL;
}
if (pUser->hKeyWindows) {
CloseHandle(pUser->hKeyWindows);
pUser->hKeyWindows = NULL;
}
}
VOID
UpdatePrinterRegAll(
LPWSTR pszPrinterName,
LPWSTR pszPort,
BOOL bDelete
)
/*++
Routine Description:
Updates everyone's [devices] and [printerports] sections (for
local printers only).
Arguments:
pszPrinterName - printer that has been added/deleted
pszPort - port name; if NULL, generate NetId
bDelete - if TRUE, delete entry instead of updating it.
Return Value:
--*/
{
WCHAR szKey[MAX_PATH];
DWORD cchKey;
DWORD i;
FILETIME ftLastWriteTime;
DWORD dwError;
//
// Go through all keys and fix them up.
//
for (i=0; TRUE; i++) {
cchKey = COUNTOF(szKey);
dwError = RegEnumKeyEx(HKEY_USERS,
i,
szKey,
&cchKey,
NULL,
NULL,
NULL,
&ftLastWriteTime);
if (dwError != ERROR_SUCCESS)
break;
if (!_wcsicmp(szKey, szDotDefault))
continue;
UpdatePrinterRegUser(NULL,
szKey,
pszPrinterName,
pszPort,
bDelete);
}
}
DWORD
UpdatePrinterRegUser(
HKEY hKeyUser,
LPWSTR pszUserKey,
LPWSTR pszPrinterName,
LPWSTR pszPort,
BOOL bDelete
)
/*++
Routine Description:
Update one user's registry. The user is specified by either
hKeyUser or pszUserKey.
Arguments:
hKeyUser - Clients user key (ignored if pszKey specified)
pszUserKey - Clients SID (Used if supplied instead of hKeyUser)
pszPrinterName - name of printe to add
pszPort - port name; if NULL, generate NetId
bDelete - if TRUE, delete entry instead of updating.
Return Value:
NOTE: We never cleanup [ports] since it is per-user
EITHER hKeyUser or pszUserKey must be valid, but not both.
--*/
{
HKEY hKeyRoot;
DWORD dwError;
WCHAR szKey[MAX_PATH];
WCHAR szBuffer[MAX_PATH];
DWORD cchKey;
DWORD dwNetId;
INIT_REG_USER InitRegUser;
InitRegUser.hKeyDevices = NULL;
InitRegUser.hKeyPrinterPorts = NULL;
InitRegUser.bDefaultSearch = FALSE;
InitRegUser.bFirstPrinterFound = TRUE;
//
// Setup the registry keys.
//
if (pszUserKey) {
wcscpy(szKey, pszUserKey);
cchKey = wcslen(szKey);
wcscpy(&szKey[cchKey], L"\\");
cchKey++;
hKeyRoot = HKEY_USERS;
} else {
cchKey = 0;
hKeyRoot = hKeyUser;
}
wcscpy(&szKey[cchKey], szRegDevicesPath);
dwError = RegOpenKeyEx(hKeyRoot,
szKey,
0,
KEY_READ|KEY_WRITE,
&InitRegUser.hKeyDevices);
if (dwError != ERROR_SUCCESS)
goto Done;
//
// Setup [PrinterPorts]
//
wcscpy(&szKey[cchKey], szRegPrinterPortsPath);
dwError = RegOpenKeyEx(hKeyRoot,
szKey,
0,
KEY_WRITE,
&InitRegUser.hKeyPrinterPorts);
if (dwError != ERROR_SUCCESS)
goto Done;
if (!bDelete) {
pszPort = CheckBadPortName( pszPort );
if( !pszPort ){
dwNetId = GetNetworkIdWorker(InitRegUser.hKeyDevices,
pszPrinterName);
}
UpdatePrinterInfo( &InitRegUser,
pszPrinterName,
pszPort,
&dwNetId );
} else {
//
// Delete the entries.
//
RegDeleteValue(InitRegUser.hKeyDevices, pszPrinterName);
RegDeleteValue(InitRegUser.hKeyPrinterPorts, pszPrinterName);
}
Done:
if( InitRegUser.hKeyDevices ){
RegCloseKey( InitRegUser.hKeyDevices );
}
if( InitRegUser.hKeyPrinterPorts ){
RegCloseKey( InitRegUser.hKeyPrinterPorts );
}
return dwError;
}
VOID
RegClearKey(
HKEY hKey
)
{
DWORD dwError;
WCHAR szValue[MAX_PATH];
DWORD cchValue;
while (TRUE) {
cchValue = COUNTOF(szValue);
dwError = RegEnumValue(hKey,
0,
szValue,
&cchValue,
NULL,
NULL,
NULL,
NULL);
if (dwError != ERROR_SUCCESS) {
SPLASSERT( dwError == ERROR_NO_MORE_ITEMS );
break;
}
if (RegDeleteValue(hKey, szValue) != ERROR_SUCCESS) {
SPLASSERT( FALSE );
break;
}
}
}
LPWSTR
CheckBadPortName(
LPWSTR pszPort
)
/*++
Routine Description:
This routine checks whether a port name should be converted to
NeXX:. Currently if the port is NULL, or "\\*," or has a space,
we convert to NeXX.
Arguments:
pszPort - port to check
Return Value:
pszPort - if port is OK.
NULL - if port needs to be converted
--*/
{
//
// If we have no pszPort, OR
// it begins with '\\' (as in \\server\share) OR
// it has a space in it OR
// it's length is greater than 5 ("LPT1:")
// Then
// use NeXX:
//
// Most 16 bit apps can't deal with long port names, since they
// allocate small buffers.
//
if( !pszPort ||
( pszPort[0] == L'\\' && pszPort[1] == L'\\' ) ||
wcschr( pszPort, L' ' ) ||
wcslen( pszPort ) > 5 ){
return NULL;
}
return pszPort;
}