|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name : rdppnutl.c
Abstract:
User-Mode RDP Module Containing Redirected Printer-Related Utilities
Author:
TadB
Revision History: --*/
#include <TSrv.h>
#include <winspool.h>
#include "rdppnutl.h"
#include "regapi.h"
#include <wchar.h>
//////////////////////////////////////////////////////////////
//
// Defines and Macros
//
//
// Spooler Service Name
//
#define SPOOLER L"Spooler"
//
// Is a character numeric?
//
#define ISNUM(c) ((c>='0')&&(c<='9'))
//////////////////////////////////////////////////////////////
//
// Globals to this Module
//
// Number of seconds to wait for the spooler to finish initializing.
DWORD SpoolerServiceTimeout = 45;
//////////////////////////////////////////////////////////////
//
// Internal Prototypes
//
// Actually performs the printer deletion.
void DeleteTSPrinters( IN PRINTER_INFO_5 *pPrinterInfo, IN DWORD count );
// Load registry settings for this module.
void LoadRDPPNUTLRegistrySettings();
// Waits until the spooler finishes initializing or until a timeout period
// elapses.
DWORD WaitForSpoolerToStart();
DWORD RDPPNUTL_RemoveAllTSPrinters() /*++
Routine Description:
Removes all TS-Redirected Printer Queues
Arguments:
Return Value:
Returns ERROR_SUCCESS on success. Error status, otherwise.
--*/ { PRINTER_INFO_5 *pPrinterInfo = NULL; DWORD cbBuf = 0; DWORD cReturnedStructs = 0; DWORD tsPrintQueueFlags; NTSTATUS status; PBYTE buf = NULL; OSVERSIONINFOEX versionInfo; unsigned char stackBuf[4 * 1024]; // Initial EnumPrinter buffer size to
// avoid two round-trip RPC's, if possible.
//
// This code should only run on server. For Pro/Personal, we can't run because it
// affects boot performance. For Pro, we clean up queues in winlogon anyway.
//
versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if (!GetVersionEx((LPOSVERSIONINFO)&versionInfo)) { status = GetLastError(); TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: GetVersionEx failed. Error: %08X.\n", status)); TS_ASSERT(FALSE); return status; } if (versionInfo.wProductType == VER_NT_WORKSTATION) { TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Skipping cleanup because not server\n")); return ERROR_SUCCESS; }
TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: RDPPNUTL_RemoveAllTSPrinters entry\n"));
//
// Load registry settings for this module.
//
LoadRDPPNUTLRegistrySettings();
//
// Wait until the spooler has finished initializing.
//
status = WaitForSpoolerToStart(); if (status != ERROR_SUCCESS) { TRACE(( DEBUG_TSHRSRV_DEBUG, "RDPPNUTL: RDPPNUTL_RemoveAllTSPrinters exiting because spooler failed to start.\n" )); return status; }
//
// Try to enumerate printers using the stack buffer, first, to avoid two
// round-trip RPC's to the spooler, if possible.
//
if (!EnumPrinters( PRINTER_ENUM_LOCAL, // Flags
NULL, // Name
5, // Print Info Type
stackBuf, // buffer
sizeof(stackBuf), // Size of buffer
&cbBuf, // Required
&cReturnedStructs)) { status = GetLastError();
//
// See if we need to allocate more room for the printer information.
//
if (status == ERROR_INSUFFICIENT_BUFFER) { buf = TSHeapAlloc(HEAP_ZERO_MEMORY, cbBuf, TS_HTAG_TSS_PRINTERINFO2);
if (buf == NULL) { TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: ALLOCMEM failed. Error: %08X.\n", GetLastError())); status = ERROR_OUTOFMEMORY; } else { pPrinterInfo = (PRINTER_INFO_5 *)buf; status = ERROR_SUCCESS; }
//
// Enumerate printers.
//
if (status == ERROR_SUCCESS) { if (!EnumPrinters( PRINTER_ENUM_LOCAL, NULL, 5, (PBYTE)pPrinterInfo, cbBuf, &cbBuf, &cReturnedStructs)) {
TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: EnumPrinters failed. Error: %08X.\n", GetLastError())); status = GetLastError(); } else { TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Second EnumPrinters succeeded.\n")); } } } else { TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: EnumPrinters failed. Error: %08X.\n", GetLastError())); } } else { TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: First EnumPrinters succeeded.\n")); status = ERROR_SUCCESS; pPrinterInfo = (PRINTER_INFO_5 *)stackBuf; }
//
// Delete all the TS printers. We allow ERROR_INSUFFICIENT_BUFFER here because
// a second invokation of EnumPrinters may have missed a few last-minute
// printer additions.
//
if ((status == ERROR_SUCCESS) || (status == ERROR_INSUFFICIENT_BUFFER)) {
DeleteTSPrinters(pPrinterInfo, cReturnedStructs);
status = ERROR_SUCCESS; }
//
// Release the printer info buffer.
//
if (buf != NULL) { TSHeapFree(buf); }
TRACE((DEBUG_TSHRSRV_DEBUG,"TShrSRV: RDPPNUTL_RemoveAllTSPrinters exit\n"));
return status; }
void DeleteTSPrinters( IN PRINTER_INFO_5 *pPrinterInfo, IN DWORD count ) /*++
Routine Description:
Actually performs the printer deletion.
Arguments:
pPrinterInfo - All printer queues on the system. count - Number of printers in pPrinterInfo
Return Value:
NA
--*/ { DWORD i; DWORD regValueDataType; DWORD sessionID; HANDLE hPrinter = NULL; DWORD bufSize; PRINTER_DEFAULTS defaults = {NULL, NULL, PRINTER_ALL_ACCESS};
TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: DeleteTSPrinters entry\n"));
for (i=0; i<count; i++) {
if (pPrinterInfo[i].pPrinterName) {
TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Checking %ws for TS printer status.\n", pPrinterInfo[i].pPrinterName));
//
// Is this a TS printer?
//
if (pPrinterInfo[i].pPortName && (pPrinterInfo[i].pPortName[0] == 'T') && (pPrinterInfo[i].pPortName[1] == 'S') && ISNUM(pPrinterInfo[i].pPortName[2])) {
TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: %ws is a TS printer.\n", pPrinterInfo[i].pPrinterName)); } else { continue; }
//
// Purge and delete the printer.
//
if (OpenPrinter(pPrinterInfo[i].pPrinterName, &hPrinter, &defaults)) { if (!SetPrinter(hPrinter, 0, NULL, PRINTER_CONTROL_PURGE) || !DeletePrinter(hPrinter)) { TRACE((DEBUG_TSHRSRV_WARN,"RDPPNUTL: Error deleting printer %ws.\n", pPrinterInfo[i].pPrinterName)); } else { TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Successfully deleted %ws.\n", pPrinterInfo[i].pPrinterName)); } ClosePrinter(hPrinter); } else { TRACE((DEBUG_TSHRSRV_ERROR, "RDPPNUTL: OpenPrinter failed for %ws. Error: %08X.\n", pPrinterInfo[i].pPrinterName, GetLastError()) ); } } else { TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Printer %ld is NULL\n", i)); } }
TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: DeleteTSPrinters exit\n")); }
void LoadRDPPNUTLRegistrySettings() /*++
Routine Description:
Load registry settings for this module.
Arguments:
Return Value: NA --*/ { HKEY regKey; DWORD dwResult; DWORD type; DWORD sz;
TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Loading registry settings.\n"));
//
// Open the registry key.
//
dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVICERDR_REG_NAME, 0, KEY_READ, ®Key); if (dwResult == ERROR_SUCCESS) { //
// Read the "wait for spooler" timeout value.
//
sz = sizeof(SpoolerServiceTimeout); dwResult = RegQueryValueEx( regKey, DEVICERDR_WAITFORSPOOLTIMEOUT, NULL, &type, (PBYTE)&SpoolerServiceTimeout, &sz ); if (dwResult != ERROR_SUCCESS){ TRACE((DEBUG_TSHRSRV_WARN, "RDPPNUTL: Failed to read spooler timeout value.: %08X.\n", dwResult)); } else { TRACE((DEBUG_TSHRSRV_WARN, "RDPPNUTL: Spooler timeout value is %ld.\n", SpoolerServiceTimeout)); }
//
// Close the reg key.
//
RegCloseKey(regKey); } else { TRACE((DEBUG_TSHRSRV_ERROR, "RDPPNUTL: Failed to open registry key: %08X.\n", dwResult)); } }
DWORD WaitForSpoolerToStart() /*++
Routine Description:
Waits until the spooler finishes initializing or until a timeout period elapses.
Arguments:
Return Value:
Returns ERROR_SUCCESS if the spooler successfully initialized. Otherwise, an error code is returned.
--*/ { SC_HANDLE scManager = NULL; SC_HANDLE hService = NULL; DWORD result = ERROR_SUCCESS; SERVICE_STATUS serviceStatus; DWORD i; QUERY_SERVICE_CONFIG *pServiceConfig = NULL; DWORD bufSize; TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Enter WaitForSpoolerToStart.\n"));
//
// Open the service control manager.
//
scManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); if (scManager == NULL) { result = GetLastError(); TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: OpenSCManager failed with %08X.\n", result)); goto CleanUpAndExit; }
//
// Open the spooler service.
//
hService = OpenService(scManager, SPOOLER, SERVICE_ALL_ACCESS); if (hService == NULL) { result = GetLastError(); TRACE((DEBUG_TSHRSRV_ERROR, "RDPPNUTL: OpenService on spooler failed with %08X.\n", result)); goto CleanUpAndExit; }
//
// If the spooler is currently running, that is all we need to know.
//
if (!QueryServiceStatus(hService, &serviceStatus)) { result = GetLastError(); TRACE((DEBUG_TSHRSRV_ERROR, "RDPPNUTL: QueryServiceStatus on spooler failed with %08X.\n", result)); goto CleanUpAndExit; } else if (serviceStatus.dwCurrentState == SERVICE_RUNNING) { TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Spooler is running.\n")); result = ERROR_SUCCESS; goto CleanUpAndExit; }
//
// Size the spooler service query configuration buffer. This API should
// fail with ERROR_INSUFFICIENT_BUFFER, so we can get the size of the
// buffer before we call the function with real parameters.
//
if (!QueryServiceConfig(hService, NULL, 0, &bufSize) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { pServiceConfig = (QUERY_SERVICE_CONFIG *)TSHeapAlloc( HEAP_ZERO_MEMORY, bufSize, TS_HTAG_TSS_SPOOLERINFO ); if (pServiceConfig == NULL) { TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: ALLOCMEM failed. Error: %08X.\n", GetLastError())); result = ERROR_OUTOFMEMORY; goto CleanUpAndExit; } } else { TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: QueryServiceConfig unexpected return.\n")); result = E_UNEXPECTED; goto CleanUpAndExit; }
//
// Get the spooler's configuration information.
//
if (!QueryServiceConfig(hService, pServiceConfig, bufSize, &bufSize)) { TRACE((DEBUG_TSHRSRV_ERROR,"RDPPNUTL: QueryServiceConfig failed: %08X.\n", GetLastError())); result = GetLastError(); goto CleanUpAndExit;
}
//
// If the spooler is not automatically configured to start on demand or
// automatically on system start then that is all we need to know.
//
if (pServiceConfig->dwStartType != SERVICE_AUTO_START) { TRACE((DEBUG_TSHRSRV_WARN,"RDPPNUTL: Spooler not configured to start.\n")); result = E_FAIL; goto CleanUpAndExit; }
//
// Poll the service status until we timeout or until the spooler
// starts.
//
for (i=0; (i<SpoolerServiceTimeout) && (serviceStatus.dwCurrentState != SERVICE_RUNNING); i++) {
//
// Sleep for a sec.
//
Sleep(1000);
TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Spooler is still initializing.\n"));
//
// Try again.
//
if (!QueryServiceStatus(hService, &serviceStatus)) { result = GetLastError(); TRACE((DEBUG_TSHRSRV_ERROR, "RDPPNUTL: QueryServiceStatus on spooler failed with %08X.\n", result)); goto CleanUpAndExit; } }
//
// Sucess if the spooler is now running.
//
if (serviceStatus.dwCurrentState == SERVICE_RUNNING) { result = ERROR_SUCCESS; } else { TRACE((DEBUG_TSHRSRV_WARN, "RDPPNUTL: Spooler is not running after a timeout or error.\n") ); result = E_FAIL; }
CleanUpAndExit:
if (pServiceConfig != NULL) { TSHeapFree(pServiceConfig); }
if (scManager != NULL) { CloseServiceHandle(scManager); }
if (hService != NULL) { CloseServiceHandle(hService); }
TRACE((DEBUG_TSHRSRV_DEBUG,"RDPPNUTL: Exit WaitForSpoolerToStart.\n"));
return result; }
|