Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1819 lines
44 KiB

/*++
Copyright (c) 1990-1994 Microsoft Corporation
All rights reserved
Module Name:
SplInit.c
Abstract:
Initialize the spooler.
Author:
Environment:
User Mode -Win32
Revision History:
4-Jan-1999 Khaleds
Added Code for optimiziting the load time of the spooler by decoupling
the startup dependency between spoolsv and spoolss
--*/
#include "precomp.h"
#pragma hdrstop
#include "local.h"
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 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";
LPWSTR szDevModes2Path = L"Printers\\DevModes2";
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
SplRegCopy(
PINIT_REG_USER pUser,
HKEY hMcConnectionKey
);
BOOL
InitializeRegUser(
LPWSTR szSubKey,
PINIT_REG_USER pUser
);
VOID
FreeRegUser(
PINIT_REG_USER pUser
);
BOOL
SetupRegForUsers(
PINIT_REG_USER pUsers,
DWORD cUsers
);
VOID
UpdateUsersDefaultPrinter(
IN PINIT_REG_USER pUser,
IN BOOL bFindDefault
);
HRESULT
IsUsersDefaultPrinter(
IN PINIT_REG_USER pUser,
IN PCWSTR pszPrinterName
);
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
UpdateLogonTimeStamp(
void
);
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 ) {
//
// We possibly should return an error here if we fail to initiatise a
// user.
//
DBGMSG( DBG_WARNING, ("SpoolerInitAll failed RegEnumKeyEx HKEY_USERS %ws %d %d\n", szSubKey, i, dwError));
SetLastError( dwError );
} else {
if (!_wcsicmp(szSubKey, szDotDefault) || wcschr(szSubKey, L'_')) {
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
DeleteOldPerMcConnections(
HKEY hConnectionKey,
HKEY hMcConnectionKey
)
/*++
Function Description - Deletes the existing permachine connections from hConnectionKey
Parameters - hConnectionKey - handle to hUserKey\Printers\Connections
Return Values - TRUE if success
FALSE otherwise.
--*/
{
BOOL bReturn = TRUE;
struct Node {
struct Node *pNext;
LPTSTR szPrinterName;
} *phead = NULL,*ptemp = NULL;
LONG lstatus;
DWORD dwRegIndex,dwNameSize,cbdata,dwquerylocal,dwType;
WCHAR szPrinterName[MAX_UNC_PRINTER_NAME];
HKEY hPrinterKey;
// Before deleting the old permachine connections, we need to record all them into
// a list. This is required because, the subkeys should not be deleted while they
// are being enumerated.
// Identifying permachine connections and saving the printernames in a list.
for (dwRegIndex = 0;
dwNameSize = COUNTOF(szPrinterName),
((lstatus = RegEnumKeyEx(hConnectionKey, dwRegIndex, szPrinterName,
&dwNameSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS);
++dwRegIndex) {
if (RegOpenKeyEx(hConnectionKey, szPrinterName, 0, KEY_ALL_ACCESS, &hPrinterKey)
!= ERROR_SUCCESS) {
bReturn = FALSE;
goto CleanUp;
}
dwquerylocal = 0;
cbdata = sizeof(dwquerylocal);
RegQueryValueEx(hPrinterKey, L"LocalConnection", NULL, &dwType,
(LPBYTE)&dwquerylocal, &cbdata);
RegCloseKey(hPrinterKey);
//
// See if it's a LocalConnection, and if it exists on the current
// machine. We don't want to delete it if it is a per-machine
// connection, since we want to keep the associated per-user
// DevMode.
//
if( ERROR_SUCCESS == RegOpenKeyEx( hMcConnectionKey,
szPrinterName,
0,
KEY_READ,
&hPrinterKey )) {
//
// The per-machine key exists. Close it and don't bother
// deleting this connection.
//
RegCloseKey( hPrinterKey );
} else {
//
// It's not a per-machine connection. Prepare to delete it.
//
if (dwquerylocal == 1) {
if (!(ptemp = (struct Node *) AllocSplMem(sizeof(struct Node)))) {
bReturn = FALSE;
goto CleanUp;
}
ptemp->pNext = phead;
phead = ptemp;
if (!(ptemp->szPrinterName = AllocSplStr(szPrinterName))) {
bReturn = FALSE;
goto CleanUp;
}
}
}
}
if (lstatus != ERROR_NO_MORE_ITEMS) {
bReturn = FALSE;
goto CleanUp;
}
// Deleting old permachine connections. The printer names are stored in the
// list pointed to by phead.
for (ptemp = phead; ptemp != NULL; ptemp = ptemp->pNext) {
if (RegDeleteKey(hConnectionKey,ptemp->szPrinterName) != ERROR_SUCCESS) {
bReturn = FALSE;
goto CleanUp;
}
}
CleanUp:
while (ptemp = phead) {
phead = phead->pNext;
if (ptemp->szPrinterName) FreeSplStr(ptemp->szPrinterName);
FreeSplMem(ptemp);
}
return bReturn;
}
BOOL
AddNewPerMcConnections(
HKEY hConnectionKey,
HKEY hMcConnectionKey
)
/*++
Function Description - Adds per-machine connections to the user hive if the connection
does not already exist.
Parameters - hConnectionKey - handle to hUserKey\Printers\Connections
hMcConnectionKey - handle to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\
Control\Print\Connections
Return Values - TRUE if success
FALSE otherwise.
--*/
{ DWORD dwRegIndex,dwNameSize,cbdata,dwType,dwlocalconnection = 1;
WCHAR szPrinterName[MAX_UNC_PRINTER_NAME];
WCHAR szConnData[MAX_UNC_PRINTER_NAME];
LONG lstatus;
BOOL bReturn = TRUE;
HKEY hNewConnKey = NULL, hPrinterKey = NULL;
for (dwRegIndex = 0;
dwNameSize = COUNTOF(szPrinterName),
((lstatus = RegEnumKeyEx(hMcConnectionKey, dwRegIndex, szPrinterName,
&dwNameSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS);
++dwRegIndex) {
RegOpenKeyEx(hConnectionKey,szPrinterName,0,KEY_READ,&hNewConnKey);
if (hNewConnKey == NULL) {
// Connection does not exist. Add one.
if (RegCreateKeyEx(hConnectionKey, szPrinterName, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL, &hNewConnKey, NULL)
|| RegOpenKeyEx(hMcConnectionKey, szPrinterName, 0, KEY_READ, &hPrinterKey)) {
bReturn = FALSE;
goto CleanUp;
}
cbdata = sizeof(szConnData);
if (RegQueryValueEx(hPrinterKey,L"Server",NULL,&dwType,(LPBYTE)szConnData,&cbdata)
|| RegSetValueEx(hNewConnKey,L"Server",0,dwType,(LPBYTE)szConnData,cbdata)) {
bReturn = FALSE;
goto CleanUp;
}
cbdata = sizeof(szConnData);
if (RegQueryValueEx(hPrinterKey,L"Provider",NULL,&dwType,(LPBYTE)szConnData,&cbdata)
|| RegSetValueEx(hNewConnKey,L"Provider",0,dwType,(LPBYTE)szConnData,cbdata)
|| RegSetValueEx(hNewConnKey,L"LocalConnection",0,REG_DWORD,
(LPBYTE)&dwlocalconnection,sizeof(dwlocalconnection))) {
bReturn = FALSE;
goto CleanUp;
}
RegCloseKey(hPrinterKey);
hPrinterKey = NULL;
}
RegCloseKey(hNewConnKey);
hNewConnKey = NULL;
}
if (lstatus != ERROR_NO_MORE_ITEMS) {
bReturn = FALSE;
}
CleanUp:
if (hNewConnKey) {
RegCloseKey(hNewConnKey);
}
if (hPrinterKey) {
RegCloseKey(hPrinterKey);
}
return bReturn;
}
BOOL
SplRegCopy(
PINIT_REG_USER pUser,
HKEY hMcConnectionKey)
/*++
Function Description - Removes old permachine connections for pUser and adds the new
permachine connections from hMcConnectionKey
Parameters - pUser - pointer to INIT_REG_USER which contains hUserKey.
hMcConnectionKey - handle to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\
Control\Print\Connections
Return Values - TRUE if success
FALSE otherwise.
--*/
{
LONG lstatus;
BOOL bReturn = TRUE;
WCHAR szRegistryConnections[] = L"Printers\\Connections";
HKEY hConnectionKey = NULL;
// Create (if not already present) and open Connections subkey
lstatus = RegCreateKeyEx(pUser->hKeyUser,
szRegistryConnections,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hConnectionKey,
NULL);
if (lstatus != ERROR_SUCCESS) {
bReturn = FALSE;
goto CleanUp;
}
if (!DeleteOldPerMcConnections(hConnectionKey,hMcConnectionKey)
|| !AddNewPerMcConnections(hConnectionKey,hMcConnectionKey)) {
bReturn = FALSE;
}
CleanUp:
if (hConnectionKey) {
RegCloseKey(hConnectionKey);
}
return bReturn;
}
BOOL
SetupRegForUsers(
PINIT_REG_USER pUsers,
DWORD cUsers)
{
DWORD cbPrinters;
DWORD cPrinters;
PBYTE pPrinters;
HKEY hMcConnectionKey = NULL;
WCHAR szMachineConnections[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Connections";
#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));
}
}
}
// Open the Key containing the current list of per-machine connections.
RegOpenKeyEx(HKEY_LOCAL_MACHINE, szMachineConnections, 0,
KEY_READ , &hMcConnectionKey);
for (i=0; i< cUsers; i++) {
// Copy Per Machine Connections into the user hive
SplRegCopy(&pUsers[i], hMcConnectionKey);
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));
}
}
}
// Close the handle to Per Machine Connections.
if (hMcConnectionKey) RegCloseKey(hMcConnectionKey);
FreeSplMem(pPrinters);
for (i=0; i< cUsers; i++) {
UpdateUsersDefaultPrinter(&pUsers[i], FALSE);
}
return TRUE;
#undef pPrinters2
#undef pPrinters4
}
VOID
UpdateUsersDefaultPrinter(
IN PINIT_REG_USER pUser,
IN BOOL bFindDefault
)
/*++
Routine Description:
Updates the default printer using the information in the
current users reg structure. If the bFindDefault flag is
specified then a default printer is located. The method for this
is first see if there is currently a default printer, then user this.
If a default printer is not found then located the first printer in
devices section, again if on exists.
Arguments:
pUser - Information about the current user, reg keys etc.
This routine assumes that hKeyWindows and hKeyDevices
are valid opened registry keys, with read access.
bFindDefault - TRUE located a default printer, FALSE the default
printer is already specified in the users reg
structure.
Return Value:
Nothing.
--*/
{
LPWSTR pszNewDefault = NULL;
//
// If a request to find the default printer.
//
if (bFindDefault) {
DWORD dwError = ERROR_SUCCESS;
DWORD cbData = sizeof(pUser->szDefaultPrinter);
//
// Check if there is a default printer.
//
dwError = RegQueryValueEx(pUser->hKeyWindows,
szDevice,
NULL,
NULL,
(PBYTE)pUser->szDefaultPrinter,
&cbData);
//
// If the device key was read and there is a non null string
// as the default printer name.
//
if (dwError == ERROR_SUCCESS && pUser->szDefaultPrinter[0] != L'\0') {
pUser->bDefaultFound = TRUE;
} else {
//
// Default was not found.
//
pUser->bDefaultFound = FALSE;
//
// If a first printer was not found.
//
if (!pUser->bFirstPrinterFound)
{
WCHAR szBuffer [MAX_PATH*2];
DWORD cbDataBuffer = sizeof(szBuffer);
DBGMSG(DBG_TRACE, ("UpdateUsersDefaultPrinter default printer not found.\n"));
cbData = COUNTOF(pUser->szFirstPrinter);
//
// Default printer was not found, find any printer
// in the devices section of the registry.
//
dwError = RegEnumValue(pUser->hKeyDevices,
0,
pUser->szFirstPrinter,
&cbData,
NULL,
NULL,
(PBYTE)szBuffer,
&cbDataBuffer);
if (dwError == ERROR_SUCCESS) {
wcscat(pUser->szFirstPrinter, L",");
wcscat(pUser->szFirstPrinter, szBuffer);
pUser->bFirstPrinterFound = TRUE;
} else {
DBGMSG(DBG_WARNING, ("UpdateUsersDefaultPrinter no printer found in devices section.\n"));
pUser->bFirstPrinterFound = FALSE;
}
}
}
}
//
// 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]));
}
}
HRESULT
IsUsersDefaultPrinter(
IN PINIT_REG_USER pUser,
IN PCWSTR pszPrinterName
)
/*++
Routine Description:
Asks if the users default printer matched the specified
printer name.
Arguments:
pCurUser - Information about the current user, reg keys etc.
This routine assumes that hKeyWindows is a valid
opened registry keys, with at least read access.
pszPrinterName - Printer name to check if it is the default printer.
Return Value:
S_OK the printer name is the default, S_FALSE the printer is not the
default, An HRESULT error code if an error occurrs attempting to
determine the default printer.
--*/
{
HRESULT hr = E_INVALIDARG;
if (pszPrinterName) {
WCHAR szBuffer[MAX_PATH*2];
DWORD dwError = ERROR_SUCCESS;
DWORD cbData = sizeof(szBuffer);
//
// Read the default printer, if one exists.
//
dwError = RegQueryValueEx(pUser->hKeyWindows,
szDevice,
NULL,
NULL,
(PBYTE)szBuffer,
&cbData);
if (dwError == ERROR_SUCCESS) {
PWSTR p = wcschr(szBuffer, L',');
if (p) {
*p = 0;
}
hr = !_wcsicmp(pszPrinterName, szBuffer) ? S_OK : S_FALSE;
} else {
hr = HRESULT_FROM_WIN32(dwError);
}
}
return hr;
}
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.
//
if ( p = wcschr(&szBuffer[cchBuffer], L',') )
*p = 0;
}
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;
}
VOID
SpoolerInitAsync(
PINIT_REG_USER pUser
)
/*++
Routine Description: Asynchronously sets up the user's registry information
Arguments: pUser - pointer to INIT_REG_USER containing user keys
Return Values: NONE
--*/
{
if (InitializeRegUser(NULL, pUser))
{
SetupRegForUsers(pUser, 1);
}
BroadcastMessage(BROADCAST_TYPE_CHANGEDEFAULT,0,0,0);
FreeRegUser(pUser);
FreeSplMem(pUser);
}
BOOL
SpoolerInit(
VOID
)
/*++
Routine Description: Initializes just the current user.
Arguments: NONE
Return Value: TRUE if initialized or async init thread created successfully
FALSE otherwise
--*/
{
BOOL bSuccess = FALSE;
DWORD dwThreadId;
HANDLE hThread;
PINIT_REG_USER pUser;
UpdateLogonTimeStamp ();
if (!(pUser = AllocSplMem(sizeof(INIT_REG_USER)))) {
return FALSE;
}
//
// Enum just the current user.
//
pUser->hKeyUser = GetClientUserHandle(KEY_READ|KEY_WRITE);
if (pUser->hKeyUser)
{
if (!Initialized)
{
//
// Process the user initialization asynchronously if the spooler
// hasn't completed it's initialization.
//
hThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE) SpoolerInitAsync,
(LPVOID) pUser, 0, &dwThreadId);
if (hThread)
{
//
// We assume that the async thread will succeed.
//
CloseHandle(hThread);
bSuccess = TRUE;
}
else
{
FreeRegUser(pUser);
FreeSplMem(pUser);
}
}
else
{
if (InitializeRegUser(NULL, pUser))
{
bSuccess = SetupRegForUsers(pUser, 1);
}
FreeRegUser(pUser);
FreeSplMem(pUser);
}
}
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;
LPWSTR p;
BOOL bSecurityLoaded = FALSE, rc = FALSE;
DWORD cbData, cbSD = 0, dwError, dwDisposition;
PSECURITY_DESCRIPTOR pSD = NULL;
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 &&
(p = wcschr(pUser->szDefaultPrinter, L',')) )
*p = 0;
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) {
RegCloseKey(pUser->hKeyUser);
pUser->hKeyUser = NULL;
}
if (pUser->hKeyDevices) {
RegCloseKey(pUser->hKeyDevices);
pUser->hKeyDevices = NULL;
}
if (pUser->hKeyPrinterPorts) {
RegCloseKey(pUser->hKeyPrinterPorts);
pUser->hKeyPrinterPorts = NULL;
}
if (pUser->hKeyWindows) {
RegCloseKey(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) || wcschr(szKey, L'_'))
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 hKeyClose = NULL;
HKEY hKeyRoot;
DWORD dwError;
WCHAR szBuffer[MAX_PATH];
DWORD dwNetId;
INIT_REG_USER InitRegUser;
ZeroMemory(&InitRegUser, sizeof(InitRegUser));
InitRegUser.hKeyDevices = NULL;
InitRegUser.hKeyPrinterPorts = NULL;
InitRegUser.bDefaultSearch = FALSE;
InitRegUser.bFirstPrinterFound = TRUE;
//
// Setup the registry keys.
//
if (pszUserKey) {
dwError = RegOpenKeyEx( HKEY_USERS,
pszUserKey,
0,
KEY_READ|KEY_WRITE,
&hKeyRoot );
if (dwError != ERROR_SUCCESS) {
goto Done;
}
hKeyClose = hKeyRoot;
} else {
hKeyRoot = hKeyUser;
}
dwError = RegOpenKeyEx(hKeyRoot,
szRegDevicesPath,
0,
KEY_READ|KEY_WRITE,
&InitRegUser.hKeyDevices);
if (dwError != ERROR_SUCCESS)
goto Done;
dwError = RegOpenKeyEx(hKeyRoot,
szRegWindowsPath,
0,
KEY_READ|KEY_WRITE,
&InitRegUser.hKeyWindows);
if (dwError != ERROR_SUCCESS)
goto Done;
//
// Setup [PrinterPorts]
//
dwError = RegOpenKeyEx(hKeyRoot,
szRegPrinterPortsPath,
0,
KEY_WRITE,
&InitRegUser.hKeyPrinterPorts);
if (dwError != ERROR_SUCCESS)
goto Done;
if (!bDelete) {
pszPort = CheckBadPortName( pszPort );
if( !pszPort ){
dwNetId = GetNetworkIdWorker(InitRegUser.hKeyDevices,
pszPrinterName);
}
InitRegUser.bFirstPrinterFound = FALSE;
UpdatePrinterInfo( &InitRegUser,
pszPrinterName,
pszPort,
&dwNetId );
UpdateUsersDefaultPrinter( &InitRegUser,
TRUE );
} else {
HKEY hKeyDevMode;
//
// Delete the entries.
//
RegDeleteValue(InitRegUser.hKeyDevices, pszPrinterName);
RegDeleteValue(InitRegUser.hKeyPrinterPorts, pszPrinterName);
//
// Check if the printer we are deleting is currently the
// default printer.
//
if (IsUsersDefaultPrinter(&InitRegUser, pszPrinterName) == S_OK) {
//
// Remove the default printer from the registry.
//
RegDeleteValue(InitRegUser.hKeyWindows, szDevice);
}
//
// Also delete DevModes2 entry from registry
//
dwError = RegOpenKeyEx( hKeyRoot,
szDevModes2Path,
0,
KEY_WRITE,
&hKeyDevMode );
if (dwError == ERROR_SUCCESS) {
//
// Delete the devmode value entry for the particular printer
//
RegDeleteValue(hKeyDevMode, pszPrinterName);
RegCloseKey(hKeyDevMode);
}
//
// Remove the per-user DevMode.
//
bSetDevModePerUser( hKeyRoot,
pszPrinterName,
NULL );
}
Done:
if( InitRegUser.hKeyDevices ){
RegCloseKey( InitRegUser.hKeyDevices );
}
if( InitRegUser.hKeyWindows ){
RegCloseKey( InitRegUser.hKeyWindows );
}
if( InitRegUser.hKeyPrinterPorts ){
RegCloseKey( InitRegUser.hKeyPrinterPorts );
}
if( hKeyClose ){
RegCloseKey( hKeyClose );
}
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) {
if( dwError != ERROR_NO_MORE_ITEMS ){
DBGMSG( DBG_WARN, ( "RegClearKey: RegEnumValue failed %d\n", dwError ));
}
break;
}
dwError = RegDeleteValue(hKey, szValue);
if( dwError != ERROR_SUCCESS) {
DBGMSG( DBG_WARN, ( "RegClearKey: RegDeleteValue failed %d\n", dwError ));
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;
}
BOOL
UpdateLogonTimeStamp(
void
)
{
long lstatus;
HKEY hProvidersKey = NULL;
FILETIME LogonTime;
LPWSTR szPrintProviders = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Providers";
LPWSTR szLogonTime = L"LogonTime";
GetSystemTimeAsFileTime (&LogonTime);
// Create (if not already present) and open Connections subkey
lstatus = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
szPrintProviders,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hProvidersKey,
NULL);
if (lstatus == ERROR_SUCCESS) {
lstatus = RegSetValueEx (hProvidersKey,
szLogonTime,
0,
REG_BINARY,
(LPBYTE) &LogonTime,
sizeof (FILETIME));
RegCloseKey(hProvidersKey);
}
return lstatus == ERROR_SUCCESS;
}