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.
1805 lines
48 KiB
1805 lines
48 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
umrdpdr.c
|
|
|
|
Abstract:
|
|
|
|
User-Mode Component for RDP Device Management
|
|
|
|
This module is included in the TermSrv tsnotify.dll, which is attached
|
|
to WINLOGON.EXE. This module, after being entered, creates a background
|
|
thread that is used by the RDPDR kernel-mode component (rdpdr.sys) to perform
|
|
user-mode device management operations.
|
|
|
|
The background thread communicates with the rdpdr.sys via IOCTL calls.
|
|
|
|
Author:
|
|
|
|
TadB
|
|
|
|
Revision History:
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <nt.h>
|
|
#include <ntioapi.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <winuser.h>
|
|
#include <string.h>
|
|
#ifdef UNITTEST
|
|
#include <stdio.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
#include <winreg.h>
|
|
#include <shlobj.h>
|
|
#include <winspool.h>
|
|
#include <rdpdr.h>
|
|
#include "drdbg.h"
|
|
#include "drdevlst.h"
|
|
#include "umrdpprn.h"
|
|
#include "umrdpdr.h"
|
|
#include "umrdpdrv.h"
|
|
#include <winsta.h>
|
|
#include "tsnutl.h"
|
|
#include "wtblobj.h"
|
|
|
|
#include "errorlog.h"
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
//
|
|
// Defines
|
|
//
|
|
|
|
//DWORD GLOBAL_DEBUG_FLAGS=0xFFFF;
|
|
DWORD GLOBAL_DEBUG_FLAGS=0x0;
|
|
|
|
// TS Network Provider Name
|
|
WCHAR ProviderName[MAX_PATH];
|
|
|
|
// Check if we are running on PTS platform
|
|
BOOL fRunningOnPTS = FALSE;
|
|
|
|
#ifndef BOOL
|
|
#define BOOL int
|
|
#endif
|
|
|
|
#define PRINTUILIBNAME TEXT("printui.dll")
|
|
|
|
// PrintUI "Run INF Install" wsprintf Format String. %s fields are, in order:
|
|
// Printer Name, Location of INF Directory, Port Name, Driver Name
|
|
#define PUI_RUNINFSTR L"/Hwzqu /if /b \"%s\" /f \"%s\\inf\\ntprint.inf\" /r \"%s\" /m \"%s\""
|
|
|
|
// Console Session ID
|
|
#define CONSOLESESSIONID 0
|
|
|
|
// Get a numeric representation of our session ID.
|
|
#if defined(UNITTEST)
|
|
ULONG g_SessionId = 0;
|
|
#else
|
|
extern ULONG g_SessionId;
|
|
#endif
|
|
#define GETTHESESSIONID() g_SessionId
|
|
|
|
// Initial size of IOCTL output buffer (big enough for the event header
|
|
// and a "buffer too small" event.
|
|
#define INITIALIOCTLOUTPUTBUFSIZE (sizeof(RDPDRDVMGR_EVENTHEADER) + \
|
|
sizeof(RDPDR_BUFFERTOOSMALL))
|
|
|
|
#if defined(UNITTEST)
|
|
// Test driver name.
|
|
#define TESTDRIVERNAME L"AGFA-AccuSet v52.3"
|
|
#define TESTPNPNAME L""
|
|
#define TESTPRINTERNAME TESTDRIVERNAME
|
|
|
|
// Test port name.
|
|
#define TESTPORTNAME L"LPT1"
|
|
#endif
|
|
|
|
// Number of ms to wait for background thread to exit.
|
|
#define KILLTHREADTIMEOUT (1000 * 8 * 60) // 8 minutes.
|
|
//#define KILLTHREADTIMEOUT (1000 * 30)
|
|
|
|
//
|
|
// Registry Locations
|
|
//
|
|
#define THISMODULEENABLEDREGKEY \
|
|
L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\Wds\\rdpwd"
|
|
|
|
#define THISMODULEENABLEDREGVALUE \
|
|
L"fEnablePrintRDR"
|
|
|
|
#define DEBUGLEVELREGKEY \
|
|
L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\Wds\\rdpwd"
|
|
|
|
#define TSNETWORKPROVIDER \
|
|
L"SYSTEM\\CurrentControlSet\\Services\\RDPNP\\NetworkProvider"
|
|
|
|
#define TSNETWORKPROVIDERNAME \
|
|
L"Name"
|
|
|
|
#define DEBUGLEVELREGVALUE \
|
|
L"UMRDPDRDebugLevel"
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
//
|
|
// Globals to this Module
|
|
//
|
|
|
|
// Event that we will use to terminate the background thread.
|
|
HANDLE CloseThreadEvent = NULL;
|
|
|
|
// Record whether shutdown is currently active.
|
|
LONG ShutdownActiveCount = 0;
|
|
|
|
BOOL g_UMRDPDR_Init = FALSE;
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
//
|
|
// Internal Prototypes
|
|
//
|
|
|
|
DWORD BackgroundThread(LPVOID tag);
|
|
|
|
BOOL SetToApplicationDesktop(
|
|
OUT HDESK *phDesk
|
|
);
|
|
|
|
void DeleteInstalledDevices(
|
|
IN PDRDEVLST deviceList
|
|
);
|
|
|
|
BOOL StopBackgroundThread(
|
|
);
|
|
|
|
BOOL HandleRemoveDeviceEvent(
|
|
IN PRDPDR_REMOVEDEVICE evt
|
|
);
|
|
|
|
BOOL UMRDPDR_ResizeBuffer(
|
|
IN OUT void **buffer,
|
|
IN DWORD bytesRequired,
|
|
IN OUT DWORD *bufferSize
|
|
);
|
|
|
|
VOID DispatchNextDeviceEvent(
|
|
IN PDRDEVLST deviceList,
|
|
IN OUT PBYTE *incomingEventBuffer,
|
|
IN OUT DWORD *incomingEventBufferSize,
|
|
IN DWORD incomingEventBufferValidBytes
|
|
);
|
|
|
|
VOID CloseThreadEventHandler(
|
|
IN HANDLE waitableObject,
|
|
IN PVOID tag
|
|
);
|
|
|
|
BOOL HandleSessionDisconnectEvent();
|
|
|
|
VOID UMRDPDR_GetUserSettings();
|
|
|
|
VOID MainLoop();
|
|
|
|
VOID CloseWaitableObjects();
|
|
|
|
VOID GetNextEvtOverlappedSignaled(
|
|
IN HANDLE waitableObject,
|
|
IN PVOID tag
|
|
);
|
|
|
|
#ifdef UNITTEST
|
|
void TellDrToAddTestPrinter();
|
|
#endif
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
BOOL g_fAutoClientLpts; // Automatically install client printers?
|
|
BOOL g_fForceClientLptDef; // Force the client printer as the default printer?
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
//
|
|
// Globals to this Module
|
|
//
|
|
|
|
HANDLE BackgroundThreadHndl = NULL;
|
|
DWORD BackGroundThreadId = 0;
|
|
|
|
// The waitable object manager.
|
|
WTBLOBJMGR WaitableObjMgr = NULL;
|
|
|
|
// True if this module is enabled.
|
|
BOOL ThisModuleEnabled = FALSE;
|
|
|
|
// List of installed devices.
|
|
DRDEVLST InstalledDevices;
|
|
|
|
// Overlapped IO structs..
|
|
OVERLAPPED GetNextEvtOverlapped;
|
|
OVERLAPPED SendClientMsgOverlapped;
|
|
|
|
// RDPDR Incoming Event Buffer
|
|
PBYTE RDPDRIncomingEventBuffer = NULL;
|
|
DWORD RDPDRIncomingEventBufferSize = 0;
|
|
|
|
// True if an IOCTL requesting the next device-related
|
|
// event is pending.
|
|
BOOL RDPDREventIOPending = FALSE;
|
|
|
|
// This module should shut down.
|
|
BOOL ShutdownFlag = FALSE;
|
|
|
|
// Token for the currently logged in user
|
|
HANDLE TokenForLoggedOnUser = NULL;
|
|
|
|
// Handle to RDPDR.SYS.
|
|
HANDLE RDPDRHndl = INVALID_HANDLE_VALUE;
|
|
|
|
BOOL
|
|
UMRDPDR_Initialize(
|
|
IN HANDLE hTokenForLoggedOnUser
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize function for this module. This function spawns a background
|
|
thread that does most of the work.
|
|
|
|
Arguments:
|
|
|
|
hTokenForLoggedOnUser - Handle to logged on user.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE on success. FALSE, otherwise.
|
|
|
|
--*/
|
|
{
|
|
BOOL result;
|
|
NTSTATUS status;
|
|
HKEY regKey;
|
|
LONG sz;
|
|
DWORD dwLastError;
|
|
|
|
|
|
/////////////////////////////////////////////////////
|
|
//
|
|
// Check a reg key to see if we should be running by
|
|
// reading a reg key. We are enabled by default.
|
|
//
|
|
DWORD enabled = TRUE;
|
|
|
|
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, THISMODULEENABLEDREGKEY, 0,
|
|
KEY_READ, ®Key);
|
|
if (status == ERROR_SUCCESS) {
|
|
sz = sizeof(enabled);
|
|
RegQueryValueEx(regKey, THISMODULEENABLEDREGVALUE, NULL,
|
|
NULL, (PBYTE)&enabled, &sz);
|
|
RegCloseKey(regKey);
|
|
}
|
|
|
|
// If we are in a non-console RDP session, then we are enabled.
|
|
ThisModuleEnabled = enabled && TSNUTL_IsProtocolRDP() &&
|
|
(!IsActiveConsoleSession());
|
|
|
|
|
|
/////////////////////////////////////////////////////
|
|
//
|
|
// Read the TS Network Provider out of the registry
|
|
//
|
|
ProviderName[0] = L'\0';
|
|
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TSNETWORKPROVIDER, 0,
|
|
KEY_READ, ®Key);
|
|
if (status == ERROR_SUCCESS) {
|
|
sz = sizeof(ProviderName);
|
|
RegQueryValueEx(regKey, TSNETWORKPROVIDERNAME, NULL,
|
|
NULL, (PBYTE)ProviderName, &sz);
|
|
RegCloseKey(regKey);
|
|
}
|
|
else {
|
|
// Should Assert here
|
|
ProviderName[0] = L'\0';
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
//
|
|
// Read the debug level out of the registry.
|
|
//
|
|
#if DBG
|
|
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEBUGLEVELREGKEY, 0,
|
|
KEY_READ, ®Key);
|
|
if (status == ERROR_SUCCESS) {
|
|
sz = sizeof(GLOBAL_DEBUG_FLAGS);
|
|
RegQueryValueEx(regKey, DEBUGLEVELREGVALUE, NULL,
|
|
NULL, (PBYTE)&GLOBAL_DEBUG_FLAGS, &sz);
|
|
RegCloseKey(regKey);
|
|
}
|
|
#endif
|
|
|
|
#ifdef UNITTEST
|
|
ThisModuleEnabled = TRUE;
|
|
#endif
|
|
|
|
// Just return if we are not enabled.
|
|
if (!ThisModuleEnabled || g_UMRDPDR_Init) {
|
|
return TRUE;
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:UMRDPDR_Initialize.\n"));
|
|
|
|
//
|
|
// If the background thread didn't exit properly the last time we were
|
|
// shut down then we risk re-entrancy by reinitializing.
|
|
//
|
|
if (BackgroundThreadHndl != NULL) {
|
|
ASSERT(FALSE);
|
|
SetLastError(ERROR_ALREADY_INITIALIZED);
|
|
return FALSE;
|
|
}
|
|
|
|
// Record the token for the logged on user.
|
|
TokenForLoggedOnUser = hTokenForLoggedOnUser;
|
|
|
|
// Reset the shutdown flag.
|
|
ShutdownFlag = FALSE;
|
|
|
|
// Load the global user settings for this user.
|
|
UMRDPDR_GetUserSettings();
|
|
|
|
// Initialize the installed device list.
|
|
DRDEVLST_Create(&InstalledDevices);
|
|
|
|
//
|
|
// Create the waitable object manager.
|
|
//
|
|
WaitableObjMgr = WTBLOBJ_CreateWaitableObjectMgr();
|
|
result = WaitableObjMgr != NULL;
|
|
|
|
//
|
|
// Initialize the supporting module for printing devices. If this module
|
|
// fails to initialize, device redirection for non-port/printing devices
|
|
// can continue to function.
|
|
//
|
|
if (result) {
|
|
UMRDPPRN_Initialize(
|
|
&InstalledDevices,
|
|
WaitableObjMgr,
|
|
TokenForLoggedOnUser
|
|
);
|
|
}
|
|
|
|
//
|
|
// Set the RDPDR incoming event buffer to the minimum starting size.
|
|
//
|
|
if (result) {
|
|
if (!UMRDPDR_ResizeBuffer(&RDPDRIncomingEventBuffer, INITIALIOCTLOUTPUTBUFSIZE,
|
|
&RDPDRIncomingEventBufferSize)) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPPRN:Cannot allocate input buffer. Error %ld\n",
|
|
GetLastError()));
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Init get next device management event overlapped io struct.
|
|
//
|
|
if (result) {
|
|
RtlZeroMemory(&GetNextEvtOverlapped, sizeof(OVERLAPPED));
|
|
GetNextEvtOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (GetNextEvtOverlapped.hEvent != NULL) {
|
|
dwLastError = WTBLOBJ_AddWaitableObject(
|
|
WaitableObjMgr, NULL,
|
|
GetNextEvtOverlapped.hEvent,
|
|
GetNextEvtOverlappedSignaled
|
|
);
|
|
if (dwLastError != ERROR_SUCCESS) {
|
|
result = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
dwLastError = GetLastError();
|
|
|
|
DBGMSG(DBG_ERROR,
|
|
("UMRDPPRN:Error creating overlapped IO event. Error: %ld\n", dwLastError));
|
|
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Init send device management event overlapped io struct.
|
|
//
|
|
RtlZeroMemory(&SendClientMsgOverlapped, sizeof(OVERLAPPED));
|
|
if (result) {
|
|
SendClientMsgOverlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (SendClientMsgOverlapped.hEvent == NULL) {
|
|
dwLastError = GetLastError();
|
|
|
|
DBGMSG(DBG_ERROR,
|
|
("UMRDPPRN:Error creating overlapped IO event. Error: %ld\n", dwLastError));
|
|
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the event that we will use to synchronize shutdown of the
|
|
// background thread.
|
|
//
|
|
if (result) {
|
|
CloseThreadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
//
|
|
// Add it to the waitable object manager.
|
|
//
|
|
if (CloseThreadEvent != NULL) {
|
|
dwLastError = WTBLOBJ_AddWaitableObject(
|
|
WaitableObjMgr, NULL,
|
|
CloseThreadEvent,
|
|
CloseThreadEventHandler
|
|
);
|
|
if (dwLastError != ERROR_SUCCESS) {
|
|
result = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
dwLastError = GetLastError();
|
|
DBGMSG(DBG_ERROR,
|
|
("UMRDPPRN:Error creating event to synchronize thread shutdown. Error: %ld\n",
|
|
dwLastError));
|
|
|
|
result = FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the background thread.
|
|
//
|
|
if (result) {
|
|
BackgroundThreadHndl = CreateThread(
|
|
NULL, 0,
|
|
(LPTHREAD_START_ROUTINE )BackgroundThread,
|
|
NULL,
|
|
0,&BackGroundThreadId
|
|
);
|
|
result = (BackgroundThreadHndl != NULL);
|
|
if (!result) {
|
|
dwLastError = GetLastError();
|
|
DBGMSG(DBG_ERROR,
|
|
("UMRDPPRN:Error creating background thread. Error: %ld\n",
|
|
dwLastError));
|
|
}
|
|
}
|
|
|
|
|
|
if (result) {
|
|
OSVERSIONINFOEX osVersionInfo;
|
|
DWORDLONG dwlConditionMask = 0;
|
|
BOOL fSuiteTerminal = FALSE;
|
|
BOOL fSuiteSingleUserTS = FALSE;
|
|
|
|
DBGMSG(DBG_INFO, ("UMRDPDR:UMRDPDR_Initialize succeeded.\n"));
|
|
|
|
ZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFOEX));
|
|
osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
osVersionInfo.wSuiteMask = VER_SUITE_TERMINAL;
|
|
VER_SET_CONDITION( dwlConditionMask, VER_SUITENAME, VER_AND );
|
|
fSuiteTerminal = VerifyVersionInfo(&osVersionInfo,VER_SUITENAME,dwlConditionMask);
|
|
osVersionInfo.wSuiteMask = VER_SUITE_SINGLEUSERTS;
|
|
fSuiteSingleUserTS = VerifyVersionInfo(&osVersionInfo,VER_SUITENAME,dwlConditionMask);
|
|
|
|
if( (TRUE == fSuiteSingleUserTS) && (FALSE == fSuiteTerminal) )
|
|
{
|
|
fRunningOnPTS = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Zero the token for the logged on user.
|
|
//
|
|
TokenForLoggedOnUser = NULL;
|
|
|
|
//
|
|
// Release the incoming RDPDR event buffer.
|
|
//
|
|
if (RDPDRIncomingEventBuffer != NULL) {
|
|
FREEMEM(RDPDRIncomingEventBuffer);
|
|
RDPDRIncomingEventBuffer = NULL;
|
|
RDPDRIncomingEventBufferSize = 0;
|
|
}
|
|
|
|
//
|
|
// Shut down the supporting module for printing device management.
|
|
//
|
|
UMRDPPRN_Shutdown();
|
|
|
|
//
|
|
// Close all waitable objects.
|
|
//
|
|
CloseWaitableObjects();
|
|
|
|
//
|
|
// Zero the background thread handle.
|
|
//
|
|
if (BackgroundThreadHndl != NULL) {
|
|
CloseHandle(BackgroundThreadHndl);
|
|
BackgroundThreadHndl = NULL;
|
|
}
|
|
|
|
SetLastError(dwLastError);
|
|
}
|
|
|
|
if (result) {
|
|
g_UMRDPDR_Init = TRUE;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
VOID
|
|
CloseWaitableObjects()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close out all waitable objects for this module.
|
|
|
|
Arguments:
|
|
|
|
NA
|
|
|
|
Return Value:
|
|
|
|
NA
|
|
|
|
--*/
|
|
{
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:CloseWaitableObjects begin.\n"));
|
|
|
|
if (CloseThreadEvent != NULL) {
|
|
ASSERT(WaitableObjMgr != NULL);
|
|
WTBLOBJ_RemoveWaitableObject(
|
|
WaitableObjMgr,
|
|
CloseThreadEvent
|
|
);
|
|
CloseHandle(CloseThreadEvent);
|
|
CloseThreadEvent = NULL;
|
|
}
|
|
|
|
if (GetNextEvtOverlapped.hEvent != NULL) {
|
|
ASSERT(WaitableObjMgr != NULL);
|
|
WTBLOBJ_RemoveWaitableObject(
|
|
WaitableObjMgr,
|
|
GetNextEvtOverlapped.hEvent
|
|
);
|
|
CloseHandle(GetNextEvtOverlapped.hEvent);
|
|
GetNextEvtOverlapped.hEvent = NULL;
|
|
}
|
|
|
|
if (SendClientMsgOverlapped.hEvent != NULL) {
|
|
CloseHandle(SendClientMsgOverlapped.hEvent);
|
|
SendClientMsgOverlapped.hEvent = NULL;
|
|
}
|
|
|
|
if (WaitableObjMgr != NULL) {
|
|
WTBLOBJ_DeleteWaitableObjectMgr(WaitableObjMgr);
|
|
WaitableObjMgr = NULL;
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:CloseWaitableObjects end.\n"));
|
|
}
|
|
|
|
BOOL
|
|
UMRDPDR_Shutdown()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close down this module. Right now, we just need to shut down the
|
|
background thread.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE on success. FALSE, otherwise.
|
|
|
|
--*/
|
|
{
|
|
BOOL backgroundThreadShutdown;
|
|
|
|
g_UMRDPDR_Init = FALSE;
|
|
//
|
|
// Just return if we are not enabled.
|
|
//
|
|
if (!ThisModuleEnabled) {
|
|
return TRUE;
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:UMRDPDR_Shutdown.\n"));
|
|
|
|
//
|
|
// Record whether shutdown is currently active.
|
|
//
|
|
if (InterlockedIncrement(&ShutdownActiveCount) > 1) {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:UMRDPDR_Shutdown already busy. Exiting.\n"));
|
|
InterlockedDecrement(&ShutdownActiveCount);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Terminate the background thread.
|
|
//
|
|
// If it won't shut down, winlogon will eventually shut it down.
|
|
// Although, this should never happen.
|
|
//
|
|
backgroundThreadShutdown = StopBackgroundThread();
|
|
if (backgroundThreadShutdown) {
|
|
|
|
//
|
|
// Make sure link to RDPDR.SYS is closed.
|
|
//
|
|
if (RDPDRHndl != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(RDPDRHndl);
|
|
RDPDRHndl = INVALID_HANDLE_VALUE;
|
|
}
|
|
RDPDRHndl = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Release the incoming RDPDR event buffer.
|
|
//
|
|
if (RDPDRIncomingEventBuffer != NULL) {
|
|
FREEMEM(RDPDRIncomingEventBuffer);
|
|
RDPDRIncomingEventBuffer = NULL;
|
|
RDPDRIncomingEventBufferSize = 0;
|
|
}
|
|
|
|
//
|
|
// Delete installed devices.
|
|
//
|
|
DeleteInstalledDevices(&InstalledDevices);
|
|
|
|
|
|
//
|
|
// Shut down the supporting module for printing device management.
|
|
//
|
|
UMRDPPRN_Shutdown();
|
|
|
|
//
|
|
// Close all waitable objects and shut down the waitable
|
|
// object manager.
|
|
//
|
|
CloseWaitableObjects();
|
|
|
|
//
|
|
// Destroy the list of installed devices.
|
|
//
|
|
DRDEVLST_Destroy(&InstalledDevices);
|
|
|
|
//
|
|
// Zero the token for the logged on user.
|
|
//
|
|
TokenForLoggedOnUser = NULL;
|
|
}
|
|
|
|
InterlockedDecrement(&ShutdownActiveCount);
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:UMRDPDR_Shutdown succeeded.\n"));
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
StopBackgroundThread()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine shuts down and cleans up after the background thread.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE on success. FALSE, otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD waitResult;
|
|
|
|
//
|
|
// Set the shutdown flag.
|
|
//
|
|
ShutdownFlag = TRUE;
|
|
|
|
//
|
|
// Set the event that signals the background thread to check the
|
|
// shut down flag.
|
|
//
|
|
if (CloseThreadEvent != NULL) {
|
|
SetEvent(CloseThreadEvent);
|
|
}
|
|
|
|
//
|
|
// Make sure it shut down.
|
|
//
|
|
if (BackgroundThreadHndl != NULL) {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Waiting for background thread to shut down.\n"));
|
|
|
|
waitResult = WaitForSingleObject(BackgroundThreadHndl, KILLTHREADTIMEOUT);
|
|
if (waitResult != WAIT_OBJECT_0) {
|
|
#if DBG
|
|
if (waitResult == WAIT_FAILED) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:Wait failed: %ld.\n", GetLastError()));
|
|
}
|
|
else if (waitResult == WAIT_ABANDONED) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:Wait abandoned\n"));
|
|
}
|
|
else if (waitResult == WAIT_TIMEOUT) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:Wait timed out.\n"));
|
|
}
|
|
else {
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:Unknown wait return status: %08X.\n", waitResult));
|
|
ASSERT(0);
|
|
}
|
|
#endif
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:Error waiting for background thread to exit.\n"));
|
|
ASSERT(FALSE);
|
|
|
|
}
|
|
else {
|
|
DBGMSG(DBG_INFO, ("UMRDPDR:Background thread shut down on its own.\n"));
|
|
CloseHandle(BackgroundThreadHndl);
|
|
BackgroundThreadHndl = NULL;
|
|
}
|
|
}
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Background thread completely shutdown.\n"));
|
|
|
|
return(BackgroundThreadHndl == NULL);
|
|
}
|
|
|
|
void DeleteInstalledDevices(
|
|
IN PDRDEVLST deviceList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete installed devices and release the installed device list.
|
|
|
|
Arguments:
|
|
|
|
devices - Devices to delete.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success. FALSE, otherwise.
|
|
|
|
--*/
|
|
{
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Removing installed devices.\n"));
|
|
while (deviceList->deviceCount > 0) {
|
|
if (deviceList->devices[0].deviceType == RDPDR_DTYP_PRINT) {
|
|
UMRDPPRN_DeleteNamedPrinterQueue(deviceList->devices[0].serverDeviceName);
|
|
}
|
|
else if (deviceList->devices[0].deviceType == RDPDR_DTYP_FILESYSTEM) {
|
|
UMRDPDRV_DeleteDriveConnection(&(deviceList->devices[0]), TokenForLoggedOnUser);
|
|
}
|
|
else if (deviceList->devices[0].deviceType != RDPDR_DRYP_PRINTPORT) {
|
|
UMRDPPRN_DeleteSerialLink( deviceList->devices[0].preferredDosName,
|
|
deviceList->devices[0].serverDeviceName,
|
|
deviceList->devices[0].clientDeviceName );
|
|
}
|
|
|
|
DRDEVLST_Remove(deviceList, 0);
|
|
}
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Done removing installed devices.\n"));
|
|
}
|
|
|
|
VOID CloseThreadEventHandler(
|
|
IN HANDLE waitableObject,
|
|
IN PVOID tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the shutdown waitable object is signaled.
|
|
|
|
Arguments:
|
|
|
|
waitableObject - Relevant waitable object.
|
|
tag - Client data, ignored.
|
|
|
|
Return Value:
|
|
|
|
NA
|
|
|
|
--*/
|
|
{
|
|
// Do nothing. The background thread should pick up the
|
|
// shutdown flag at the top of its loop.
|
|
}
|
|
|
|
VOID GetNextEvtOverlappedSignaled(
|
|
IN HANDLE waitableObject,
|
|
IN PVOID tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by the Waitable Object Manager when a pending IO event from RDPDR.SYS
|
|
has completed.
|
|
|
|
Arguments:
|
|
|
|
waitableObject - Relevant waitable object.
|
|
tag - Client data, ignored.
|
|
|
|
Return Value:
|
|
|
|
NA
|
|
|
|
--*/
|
|
{
|
|
DWORD bytesReturned;
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:GetNextEvtOverlappedSignaled begin.\n"));
|
|
|
|
//
|
|
// IO from RDPDR.SYS is no longer pending.
|
|
//
|
|
RDPDREventIOPending = FALSE;
|
|
|
|
//
|
|
// Dispatch the event.
|
|
//
|
|
if (GetOverlappedResult(RDPDRHndl, &GetNextEvtOverlapped,
|
|
&bytesReturned, FALSE)) {
|
|
|
|
ResetEvent(GetNextEvtOverlapped.hEvent);
|
|
|
|
DispatchNextDeviceEvent(
|
|
&InstalledDevices,
|
|
&RDPDRIncomingEventBuffer,
|
|
&RDPDRIncomingEventBufferSize,
|
|
bytesReturned
|
|
);
|
|
}
|
|
else {
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:GetOverlappedResult failed: %ld.\n",
|
|
GetLastError()));
|
|
ASSERT(0);
|
|
ShutdownFlag = TRUE;
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:GetNextEvtOverlappedSignaled end.\n"));
|
|
}
|
|
|
|
DWORD BackgroundThread(
|
|
IN PVOID tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This thread handles all device-install/uninstall-related issues.
|
|
|
|
Arguments:
|
|
|
|
tag - Ignored.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
BOOL result=TRUE;
|
|
HDESK hDesk = NULL;
|
|
WCHAR drPath[MAX_PATH+1];
|
|
DWORD dwLastError;
|
|
DWORD dwFailedLineNumber;
|
|
HDESK hDeskSave = NULL;
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:BackgroundThread.\n"));
|
|
|
|
//
|
|
// Create the path to the "dr."
|
|
//
|
|
wsprintf(drPath, L"\\\\.\\%s%s%ld",
|
|
RDPDRDVMGR_W32DEVICE_NAME_U,
|
|
RDPDYN_SESSIONIDSTRING,
|
|
GETTHESESSIONID());
|
|
ASSERT(wcslen(drPath) <= MAX_PATH);
|
|
|
|
//
|
|
// Open a connection to the RDPDR.SYS device manager device.
|
|
//
|
|
RDPDRHndl = CreateFile(
|
|
drPath,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
|
NULL
|
|
);
|
|
if (RDPDRHndl == INVALID_HANDLE_VALUE) {
|
|
dwLastError = GetLastError();
|
|
dwFailedLineNumber = __LINE__;
|
|
|
|
DBGMSG(DBG_ERROR,
|
|
("Error opening RDPDR device manager component. Error: %ld\n", dwLastError));
|
|
|
|
result = FALSE;
|
|
goto CleanupAndExit;
|
|
}
|
|
|
|
//
|
|
// Save the current thread desktop.
|
|
//
|
|
hDeskSave = GetThreadDesktop(GetCurrentThreadId());
|
|
if (hDeskSave == NULL) {
|
|
dwLastError = GetLastError();
|
|
dwFailedLineNumber = __LINE__;
|
|
result = FALSE;
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR: GetThreadDesktop: %08X\n", dwLastError));
|
|
goto CleanupAndExit;
|
|
}
|
|
|
|
//
|
|
// Set the current desktop for our thread to the application desktop.
|
|
//
|
|
if (!SetToApplicationDesktop(&hDesk)) {
|
|
dwLastError = GetLastError();
|
|
dwFailedLineNumber = __LINE__;
|
|
result = FALSE;
|
|
goto CleanupAndExit;
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:After setting the application desktop.\n"));
|
|
|
|
//
|
|
// Enter the main loop until it completes, indicating time to exit.
|
|
//
|
|
MainLoop();
|
|
|
|
DBGMSG(DBG_TRACE, ("UMDRPDR:Exiting background thread.\n"));
|
|
|
|
//
|
|
// Close the application desktop handle.
|
|
//
|
|
CleanupAndExit:
|
|
if (!result) {
|
|
SetLastError(dwLastError);
|
|
TsLogError(EVENT_NOTIFY_PRINTER_REDIRECTION_FAILED, EVENTLOG_ERROR_TYPE,
|
|
0, NULL, dwFailedLineNumber);
|
|
}
|
|
|
|
if (RDPDRHndl != INVALID_HANDLE_VALUE) {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Closing connection to RDPDR.SYS.\n"));
|
|
if (!CloseHandle(RDPDRHndl)) {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Error closing connection to RDPDR.SYS: %ld.\n",
|
|
GetLastError()));
|
|
}
|
|
else {
|
|
RDPDRHndl = INVALID_HANDLE_VALUE;
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Connection to RDPDR.SYS successfully closed\n"));
|
|
}
|
|
}
|
|
|
|
if (hDeskSave != NULL) {
|
|
if (!SetThreadDesktop(hDeskSave)) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:SetThreadDesktop: %08X\n", GetLastError()));
|
|
}
|
|
}
|
|
|
|
if (hDesk != NULL) {
|
|
if (!CloseDesktop(hDesk)) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:CloseDesktop: %08X\n", GetLastError()));
|
|
}
|
|
else {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:CloseDesktop succeeded.\n"));
|
|
}
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("UMDRPDR:Done exiting background thread.\n"));
|
|
return result;
|
|
}
|
|
|
|
VOID
|
|
MainLoop()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Main loop for the background thread.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NA
|
|
|
|
--*/
|
|
{
|
|
DWORD waitResult;
|
|
BOOL result;
|
|
DWORD bytesReturned;
|
|
|
|
//
|
|
// Reset the flag, indicating that IO to RDPDR is not yet pending.
|
|
//
|
|
RDPDREventIOPending = FALSE;
|
|
|
|
//
|
|
// Loop until the background thread should exit and this module should
|
|
// shut down.
|
|
//
|
|
while (!ShutdownFlag) {
|
|
|
|
//
|
|
// Send an IOCTL to RDPDR.SYS to get the next "device management event."
|
|
//
|
|
if (!RDPDREventIOPending) {
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Sending IOCTL to RDPDR.\n"));
|
|
result = DeviceIoControl(
|
|
RDPDRHndl,
|
|
IOCTL_RDPDR_GETNEXTDEVMGMTEVENT,
|
|
NULL,
|
|
0,
|
|
RDPDRIncomingEventBuffer,
|
|
RDPDRIncomingEventBufferSize,
|
|
&bytesReturned, &GetNextEvtOverlapped
|
|
);
|
|
|
|
//
|
|
// If the IOCTL finished.
|
|
//
|
|
if (result && !ShutdownFlag) {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:DeviceIoControl no pending IO. Data ready.\n"));
|
|
|
|
if (!ResetEvent(GetNextEvtOverlapped.hEvent)) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR: ResetEvent: %08X\n",
|
|
GetLastError()));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
DispatchNextDeviceEvent(
|
|
&InstalledDevices,
|
|
&RDPDRIncomingEventBuffer,
|
|
&RDPDRIncomingEventBufferSize,
|
|
bytesReturned
|
|
);
|
|
}
|
|
else if (!result && (GetLastError() != ERROR_IO_PENDING)) {
|
|
|
|
DBGMSG(DBG_ERROR, ("UMRDPPRN:DeviceIoControl failed. Error: %ld\n",
|
|
GetLastError()));
|
|
|
|
TsLogError(
|
|
EVENT_NOTIFY_PRINTER_REDIRECTION_FAILED, EVENTLOG_ERROR_TYPE,
|
|
0, NULL, __LINE__
|
|
);
|
|
|
|
//
|
|
// Shut down the background thread and the module, in general.
|
|
//
|
|
ShutdownFlag = TRUE;
|
|
}
|
|
else {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:DeviceIoControl indicated IO pending.\n"));
|
|
|
|
RDPDREventIOPending = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If IO to RDPDR.SYS is pending, then wait for one of our waitable objects to
|
|
// become signaled. This way, the shutdown event and data from RDPDR.SYS gets
|
|
// priority.
|
|
//
|
|
if (!ShutdownFlag && RDPDREventIOPending) {
|
|
if (WTBLOBJ_PollWaitableObjects(WaitableObjMgr) != ERROR_SUCCESS) {
|
|
ShutdownFlag = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DispatchNextDeviceEvent(
|
|
IN PDRDEVLST deviceList,
|
|
IN OUT PBYTE *incomingEventBuffer,
|
|
IN OUT DWORD *incomingEventBufferSize,
|
|
IN DWORD incomingEventBufferValidBytes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch the next device-related event from RDPDR.SYS.
|
|
|
|
Arguments:
|
|
|
|
deviceList - Master list of installed devices.
|
|
incomingEventBuffer - Incoming event buffer.
|
|
incomingEventBufferSize - Incoming event buffer size
|
|
incomingEventBufferValidBytes - Number of valid bytes in the event
|
|
buffer.
|
|
|
|
Return Value:
|
|
|
|
NA
|
|
|
|
--*/
|
|
{
|
|
PRDPDR_PRINTERDEVICE_SUB printerAnnounceEvent;
|
|
PRDPDR_PORTDEVICE_SUB portAnnounceEvent;
|
|
PRDPDR_DRIVEDEVICE_SUB driveAnnounceEvent;
|
|
PRDPDR_REMOVEDEVICE removeDeviceEvent;
|
|
PRDPDRDVMGR_EVENTHEADER eventHeader;
|
|
PRDPDR_BUFFERTOOSMALL bufferTooSmallEvent;
|
|
DWORD eventDataSize;
|
|
PBYTE eventData;
|
|
DWORD lastError = ERROR_SUCCESS;
|
|
DWORD dwFailedLineNumber;
|
|
DWORD errorEventCode;
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:DispatchNextDeviceEvent.\n"));
|
|
|
|
//
|
|
// The first few bytes of the result buffer are the header.
|
|
//
|
|
ASSERT(incomingEventBufferValidBytes >= sizeof(RDPDRDVMGR_EVENTHEADER));
|
|
eventHeader = (PRDPDRDVMGR_EVENTHEADER)(*incomingEventBuffer);
|
|
eventData = *incomingEventBuffer + sizeof(RDPDRDVMGR_EVENTHEADER);
|
|
eventDataSize = incomingEventBufferValidBytes - sizeof(RDPDRDVMGR_EVENTHEADER);
|
|
|
|
//
|
|
// Dispatch the event.
|
|
//
|
|
switch(eventHeader->EventType) {
|
|
|
|
case RDPDREVT_BUFFERTOOSMALL :
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Buffer too small msg received.\n"));
|
|
|
|
ASSERT((incomingEventBufferValidBytes - sizeof(RDPDRDVMGR_EVENTHEADER)) >=
|
|
sizeof(RDPDR_BUFFERTOOSMALL));
|
|
bufferTooSmallEvent = (PRDPDR_BUFFERTOOSMALL)(*incomingEventBuffer +
|
|
sizeof(RDPDRDVMGR_EVENTHEADER));
|
|
if (!UMRDPDR_ResizeBuffer(incomingEventBuffer, bufferTooSmallEvent->RequiredSize,
|
|
incomingEventBufferSize)) {
|
|
ShutdownFlag = TRUE;
|
|
|
|
lastError = ERROR_INSUFFICIENT_BUFFER;
|
|
errorEventCode = EVENT_NOTIFY_INSUFFICIENTRESOURCES;
|
|
dwFailedLineNumber = __LINE__;
|
|
}
|
|
break;
|
|
|
|
case RDPDREVT_PRINTERANNOUNCE :
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Printer announce msg received.\n"));
|
|
|
|
ASSERT(eventDataSize >= sizeof(RDPDR_PRINTERDEVICE_SUB));
|
|
printerAnnounceEvent = (PRDPDR_PRINTERDEVICE_SUB)eventData;
|
|
if (!UMRDPPRN_HandlePrinterAnnounceEvent(
|
|
printerAnnounceEvent
|
|
)) {
|
|
}
|
|
break;
|
|
|
|
case RDPDREVT_PORTANNOUNCE :
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Port announce event received.\n"));
|
|
|
|
ASSERT(eventDataSize >= sizeof(PRDPDR_PORTDEVICE_SUB));
|
|
portAnnounceEvent = (PRDPDR_PORTDEVICE_SUB)eventData;
|
|
UMRDPPRN_HandlePrintPortAnnounceEvent(portAnnounceEvent);
|
|
break;
|
|
|
|
case RDPDREVT_DRIVEANNOUNCE :
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Drive announce event received.\n"));
|
|
|
|
ASSERT(eventDataSize >= sizeof(PRDPDR_DRIVEDEVICE_SUB));
|
|
driveAnnounceEvent = (PRDPDR_DRIVEDEVICE_SUB)eventData;
|
|
UMRDPDRV_HandleDriveAnnounceEvent(deviceList, driveAnnounceEvent,
|
|
TokenForLoggedOnUser);
|
|
break;
|
|
|
|
case RDPDREVT_REMOVEDEVICE :
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Remove device event received.\n"));
|
|
|
|
ASSERT(eventDataSize >= sizeof(RDPDR_REMOVEDEVICE));
|
|
removeDeviceEvent = (PRDPDR_REMOVEDEVICE)eventData;
|
|
HandleRemoveDeviceEvent(removeDeviceEvent);
|
|
break;
|
|
|
|
case RDPDREVT_SESSIONDISCONNECT :
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Session disconnected event received.\n"));
|
|
|
|
// There isn't any event data associated with a session disconnect event.
|
|
ASSERT(eventDataSize == 0);
|
|
HandleSessionDisconnectEvent();
|
|
break;
|
|
|
|
default :
|
|
|
|
DBGMSG(DBG_WARN, ("UMRDPDR:Unrecognized msg from RDPDR.SYS.\n"));
|
|
}
|
|
|
|
//
|
|
// Log an error if there is one to be logged.
|
|
//
|
|
if (lastError != ERROR_SUCCESS) {
|
|
|
|
SetLastError(lastError);
|
|
TsLogError(
|
|
errorEventCode,
|
|
EVENTLOG_ERROR_TYPE,
|
|
0,
|
|
NULL,
|
|
dwFailedLineNumber
|
|
);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
HandleSessionDisconnectEvent()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles session disconnect device management by deleting all known
|
|
session devices.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Return TRUE on success. FALSE, otherwise.
|
|
|
|
--*/
|
|
{
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:HandleSessionDisconnectEvent.\n"));
|
|
|
|
//
|
|
// Delete installed devices.
|
|
//
|
|
DeleteInstalledDevices(&InstalledDevices);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
HandleRemoveDeviceEvent(
|
|
IN PRDPDR_REMOVEDEVICE evt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle a device removal component from RDPDR.SYS.
|
|
|
|
Arguments:
|
|
|
|
removeDeviceEvent - Device removal event.
|
|
|
|
Return Value:
|
|
|
|
Return TRUE on success. FALSE, otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD ofs;
|
|
BOOL result;
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:HandleRemoveDeviceEvent.\n"));
|
|
|
|
// Find the device in our device list via the client-assigned device ID.
|
|
if (DRDEVLST_FindByClientDeviceID(&InstalledDevices, evt->deviceID, &ofs)) {
|
|
|
|
//
|
|
// Switch on the type of device being removed.
|
|
//
|
|
switch(InstalledDevices.devices[ofs].deviceType)
|
|
{
|
|
case RDPDR_DTYP_PRINT :
|
|
|
|
DBGMSG(DBG_WARN, ("UMRDPDR:Printer queue %ws removed.\n",
|
|
InstalledDevices.devices[ofs].serverDeviceName));
|
|
|
|
if (UMRDPPRN_DeleteNamedPrinterQueue(
|
|
InstalledDevices.devices[ofs].serverDeviceName)) {
|
|
DRDEVLST_Remove(&InstalledDevices, ofs);
|
|
result = TRUE;
|
|
}
|
|
else {
|
|
result = FALSE;
|
|
}
|
|
break;
|
|
|
|
case RDPDR_DTYP_SERIAL :
|
|
case RDPDR_DTYP_PARALLEL :
|
|
DBGMSG(DBG_WARN, ("UMRDPDR:Serial port %ws removed.\n",
|
|
InstalledDevices.devices[ofs].serverDeviceName));
|
|
|
|
if (UMRDPPRN_DeleteSerialLink( InstalledDevices.devices[ofs].preferredDosName,
|
|
InstalledDevices.devices[ofs].serverDeviceName,
|
|
InstalledDevices.devices[ofs].clientDeviceName )) {
|
|
DRDEVLST_Remove(&InstalledDevices, ofs);
|
|
result = TRUE;
|
|
}
|
|
else {
|
|
result = FALSE;
|
|
}
|
|
break;
|
|
|
|
case RDPDR_DRYP_PRINTPORT :
|
|
|
|
DBGMSG(DBG_WARN, ("UMRDPDR:Parallel port %ws removed.\n",
|
|
InstalledDevices.devices[ofs].serverDeviceName));
|
|
DRDEVLST_Remove(&InstalledDevices, ofs);
|
|
result = TRUE;
|
|
break;
|
|
|
|
case RDPDR_DTYP_FILESYSTEM:
|
|
|
|
DBGMSG(DBG_WARN, ("UMRDPDR:Redirected drive %ws removed.\n",
|
|
InstalledDevices.devices[ofs].serverDeviceName));
|
|
|
|
if (UMRDPDRV_DeleteDriveConnection(&(InstalledDevices.devices[ofs]),
|
|
TokenForLoggedOnUser)) {
|
|
DRDEVLST_Remove(&InstalledDevices, ofs);
|
|
result = TRUE;
|
|
}
|
|
else {
|
|
result = FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
result = FALSE;
|
|
DBGMSG(DBG_WARN, ("UMRDPDR:Remove event received for unknown device type %ld.\n",
|
|
InstalledDevices.devices[ofs].deviceType));
|
|
|
|
}
|
|
}
|
|
else {
|
|
result = FALSE;
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:Cannot find device with id %ld for removal.\n",
|
|
evt->deviceID));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BOOL
|
|
UMRDPDR_SendMessageToClient(
|
|
IN PVOID msg,
|
|
IN DWORD msgSize
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Send a message to the TS client corresponding to this session, via the kernel
|
|
mode component.
|
|
|
|
Arguments:
|
|
|
|
msg - The message.
|
|
msgSize - Size (in bytes) of message.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success. FALSE otherwise.
|
|
--*/
|
|
{
|
|
BOOL result;
|
|
BYTE outBuf[MAX_PATH];
|
|
DWORD bytesReturned;
|
|
BOOL wait;
|
|
DWORD waitResult;
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:UMRDPDR_SendMessageToClient.\n"));
|
|
|
|
//
|
|
// Send the "client message" IOCTL to RDPDR.
|
|
//
|
|
|
|
result = DeviceIoControl(
|
|
RDPDRHndl,
|
|
IOCTL_RDPDR_CLIENTMSG,
|
|
msg,
|
|
msgSize,
|
|
outBuf,
|
|
sizeof(outBuf),
|
|
&bytesReturned,
|
|
&SendClientMsgOverlapped
|
|
);
|
|
|
|
//
|
|
// See if we need to wait for the IO complete. RDPDR.SYS is designed to
|
|
// return immediately, in response to this IOCTL.
|
|
//
|
|
if (result) {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:DeviceIoControl no pending IO. Data ready.\n"));
|
|
wait = FALSE;
|
|
result = TRUE;
|
|
}
|
|
else if (!result && (GetLastError() != ERROR_IO_PENDING)) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPPRN:DeviceIoControl Failed. Error: %ld\n",
|
|
GetLastError()));
|
|
|
|
TsLogError(EVENT_NOTIFY_PRINTER_REDIRECTION_FAILED, EVENTLOG_ERROR_TYPE, 0, NULL, __LINE__);
|
|
wait = FALSE;
|
|
result = FALSE;
|
|
}
|
|
else {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:DeviceIoControl indicated IO pending.\n"));
|
|
wait = TRUE;
|
|
result = TRUE;
|
|
}
|
|
|
|
//
|
|
// Wait for the IO to complete.
|
|
//
|
|
if (wait) {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:UMRDPDR_SendMessageToClient IO is pending.\n"));
|
|
waitResult = WaitForSingleObject(SendClientMsgOverlapped.hEvent, INFINITE);
|
|
if (waitResult != WAIT_OBJECT_0) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPPRN:RDPDR.SYS failed client message IOCTL. Error: %ld\n",
|
|
GetLastError()));
|
|
|
|
TsLogError(EVENT_NOTIFY_PRINTER_REDIRECTION_FAILED, EVENTLOG_ERROR_TYPE, 0, NULL, __LINE__);
|
|
result = FALSE;
|
|
}
|
|
else {
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Client message sent successfully.\n"));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
BOOL
|
|
UMRDPDR_ResizeBuffer(
|
|
IN OUT void **buffer,
|
|
IN DWORD bytesRequired,
|
|
IN OUT DWORD *bufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Make sure a buffer is large enough.
|
|
|
|
Arguments:
|
|
|
|
buffer - The buffer.
|
|
bytesRequired - Required size.
|
|
bufferSize - Current buffer size.
|
|
|
|
Return Value:
|
|
|
|
Returns FALSE if buffer cannot be resized.
|
|
|
|
--*/
|
|
{
|
|
BOOL result;
|
|
|
|
if (*bufferSize < bytesRequired) {
|
|
if (*buffer == NULL) {
|
|
*buffer = ALLOCMEM(bytesRequired);
|
|
}
|
|
else {
|
|
void *pTmp = REALLOCMEM(*buffer, bytesRequired);
|
|
if (pTmp != NULL) {
|
|
*buffer = pTmp;
|
|
} else {
|
|
FREEMEM(*buffer);
|
|
*buffer = NULL;
|
|
}
|
|
}
|
|
if (*buffer == NULL) {
|
|
*bufferSize = 0;
|
|
DBGMSG(DBG_ERROR, ("UMRDPPRN:Error resizing buffer. Error: %ld\n",
|
|
GetLastError()));
|
|
result = FALSE;
|
|
}
|
|
else {
|
|
result = TRUE;
|
|
*bufferSize = bytesRequired;
|
|
}
|
|
} else {
|
|
result = TRUE;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
BOOL
|
|
SetToApplicationDesktop(
|
|
OUT HDESK *phDesk
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the current desktop for our thread to the application desktop.
|
|
Callers of this function should call CloseDesktop with the returned
|
|
desktop handle when they are done with the desktop.
|
|
|
|
Arguments:
|
|
|
|
phDesk - Pointer to the application desktop.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE on success. FALSE, otherwise.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(phDesk != NULL);
|
|
|
|
*phDesk = OpenDesktopW(L"default", 0, FALSE,
|
|
DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW |
|
|
DESKTOP_CREATEMENU | DESKTOP_WRITEOBJECTS |
|
|
STANDARD_RIGHTS_REQUIRED);
|
|
if (*phDesk == NULL) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPPRN:Failed to open desktop. Error: %ld\n",
|
|
GetLastError()));
|
|
return FALSE;
|
|
}
|
|
else if (!SetThreadDesktop(*phDesk)) {
|
|
DBGMSG(DBG_ERROR, ("UMRDPPRN:Failed to set current thread desktop. Error: %ld\n",
|
|
GetLastError()));
|
|
CloseDesktop(*phDesk);
|
|
*phDesk = NULL;
|
|
return FALSE;
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UMRDPDR_GetUserSettings()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets the flags to determine whether we do automatic installation
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
WINSTATIONCONFIG WinStationConfig;
|
|
ULONG ReturnLength;
|
|
HANDLE hServer;
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:UMRDPDR_GetUserFlags called.\n"));
|
|
|
|
g_fAutoClientLpts = FALSE;
|
|
g_fForceClientLptDef = FALSE;
|
|
|
|
hServer = WinStationOpenServer(NULL);
|
|
|
|
if (hServer) {
|
|
Status = WinStationQueryInformation(hServer, g_SessionId,
|
|
WinStationConfiguration, &WinStationConfig,
|
|
sizeof(WinStationConfig), &ReturnLength);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
g_fAutoClientLpts = WinStationConfig.User.fAutoClientLpts;
|
|
g_fForceClientLptDef = WinStationConfig.User.fForceClientLptDef;
|
|
} else {
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:Error querying user settings\n"));
|
|
}
|
|
WinStationCloseServer(hServer);
|
|
} else {
|
|
DBGMSG(DBG_ERROR, ("UMRDPDR:Opening winstation\n"));
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Client printers will%sbe mapped\n",
|
|
g_fAutoClientLpts ? " " : " not "));
|
|
DBGMSG(DBG_TRACE, ("UMRDPDR:Client printers will%sbe made default\n",
|
|
g_fForceClientLptDef ? " " : " not "));
|
|
}
|
|
BOOL UMRDPDR_fAutoInstallPrinters()
|
|
{
|
|
return g_fAutoClientLpts;
|
|
}
|
|
BOOL UMRDPDR_fSetClientPrinterDefault()
|
|
{
|
|
return g_fForceClientLptDef;
|
|
}
|
|
|
|
#if DBG
|
|
VOID DbgMsg(CHAR *msgFormat, ...)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Print debug output.
|
|
|
|
Arguments:
|
|
|
|
pathBuffer - Address of a buffer to receive the path name selected by
|
|
the user. The size of this buffer is assumed to be
|
|
MAX_PATH bytes. This buffer should contain the default
|
|
path.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE on success. FALSE, otherwise.
|
|
|
|
--*/
|
|
{
|
|
CHAR msgText[256];
|
|
va_list vargs;
|
|
|
|
va_start(vargs, msgFormat);
|
|
wvsprintfA(msgText, msgFormat, vargs);
|
|
va_end( vargs );
|
|
|
|
if (*msgText)
|
|
OutputDebugStringA("UMRDPDR: ");
|
|
OutputDebugStringA(msgText);
|
|
}
|
|
#endif
|
|
|
|
/*++
|
|
|
|
Unit-Test Entry Point
|
|
|
|
--*/
|
|
#ifdef UNITTEST
|
|
void __cdecl main(int argc, char **argv)
|
|
{
|
|
BOOL killLoop = FALSE;
|
|
int i;
|
|
BOOL result;
|
|
NTSTATUS ntStatus;
|
|
HKEY regKey;
|
|
LONG regValueSize;
|
|
LONG status;
|
|
HANDLE pHandle;
|
|
HANDLE tokenHandle;
|
|
|
|
TsInitLogging();
|
|
|
|
//
|
|
// Check the command-line args to see what test we are performing.
|
|
//
|
|
if ((argc > 1) && strcmp(argv[1], "\\?")) {
|
|
if (!strcmp(argv[1], "AddPrinter")) {
|
|
|
|
TellDrToAddTestPrinter();
|
|
exit(-1);
|
|
|
|
}
|
|
else if (!strcmp(argv[1], "StandAlone")) {
|
|
pHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
|
|
GetCurrentProcessId());
|
|
if (!OpenProcessToken(pHandle, TOKEN_ALL_ACCESS, &tokenHandle)) {
|
|
OutputDebugString(TEXT("Error opening process token. Exiting\n"));
|
|
exit(-1);
|
|
}
|
|
|
|
UMRDPDR_Initialize(tokenHandle);
|
|
while (!killLoop) {
|
|
Sleep(100);
|
|
}
|
|
|
|
UMRDPDR_Shutdown();
|
|
OutputDebugString(L"UMRDPDR:Exiting.\r\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
else {
|
|
printf("\\? for command line parameters.\n");
|
|
printf("AddPrinter to tell RDPDR.SYS to generate a test printer.\n");
|
|
printf("StandAlone to run normally, but stand-alone.\n");
|
|
printf("UnitTest to run a simple unit-test.\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void TellDrToAddTestPrinter()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function uses a debug IOCTL to tell RDPDR to generate a test
|
|
printer event.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
WCHAR drPath[MAX_PATH+1];
|
|
UNICODE_STRING uncDrPath;
|
|
HANDLE drHndl = INVALID_HANDLE_VALUE;
|
|
BOOL result;
|
|
OBJECT_ATTRIBUTES fileAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS ntStatus;
|
|
BYTE inbuf[MAX_PATH];
|
|
BYTE outbuf[MAX_PATH];
|
|
DWORD bytesReturned;
|
|
|
|
//
|
|
// Create the path to the "dr."
|
|
//
|
|
wsprintf(drPath, L"\\\\.\\%s%s%ld",
|
|
RDPDRDVMGR_W32DEVICE_NAME_U,
|
|
RDPDYN_SESSIONIDSTRING,
|
|
0x9999);
|
|
|
|
ASSERT(wcslen(drPath) <= MAX_PATH);
|
|
|
|
//
|
|
// Open a connection to the RDPDR.SYS device manager device.
|
|
//
|
|
drHndl = CreateFile(
|
|
drPath,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
if (drHndl == INVALID_HANDLE_VALUE) {
|
|
TsLogError(EVENT_NOTIFY_RDPDR_FAILED, EVENTLOG_ERROR_TYPE, 0, NULL, __LINE__);
|
|
DBGMSG(DBG_ERROR, ("UMRDPPRN:Error opening RDPDR device manager component. Error: %ld\n",
|
|
GetLastError()));
|
|
}
|
|
else {
|
|
|
|
// Tell the DR to add a new test printer.
|
|
if (!DeviceIoControl(drHndl, IOCTL_RDPDR_DBGADDNEWPRINTER, inbuf,
|
|
0, outbuf, sizeof(outbuf), &bytesReturned, NULL)) {
|
|
|
|
TsLogError(EVENT_NOTIFY_RDPDR_FAILED, EVENTLOG_ERROR_TYPE, 0, NULL, __LINE__);
|
|
|
|
DBGMSG(DBG_ERROR, ("UMRDPPRN:Error sending IOCTL to device manager component. Error: %ld\n",
|
|
GetLastError()));
|
|
}
|
|
// Clean up.
|
|
CloseHandle(drHndl);
|
|
}
|
|
}
|
|
#endif
|
|
|