/* Copyright (c) 1997 Microsoft Corporation Module Name: ui.c Abstract: This file contains the mainloop for the Client Side Caching agent. The code has to mesh with the system startup, logon and logoff and these are different on NT and win9x. This file and all others in the reint directory have been written such that for NT they call the wide character win32 APIs while for win9x they call ANSI APIs. The agent runs as a thread in the context of winlogon.exe. CSCDLL.DLL resgisters itself to recieve a call from winlogon when a user logs on. The call is on a separate thread and is impersonated as the logged on user. This thread eventually calls reint_winmain which loops for ever, till the system is about to shutdown. file ntstuff.c contains the interface which is exposed to winlogon. On every logon this interface gets called, at which point, all the info necessary to impersonate the logged on user is obtained and kept in an in memory list of logged on users. The list also contains the SID for Local System. For doing sparse filling and Inode, the agent looks in the database to see which of the users on the list have read access right for the given file and uses that to fill the file. Author(s): Trent Gray Donald/Felix Andrews/Shishir Pardikar 1-9-1994 Environment: Win32 (user-mode) DLL Revision History: NT source formatting Shishir Pardikar 2-19-97 Winlogon Integration Shishir Pardikar 10-19-97 --*/ #include "pch.h" #pragma hdrstop #include "resource.h" #include "traynoti.h" #include #include "lib3.h" #include "reint.h" #include "utils.h" #include "strings.h" #include "cscuiext.h" #include #include // // defines/macros useed in this file // #if (_TCHAR != wchar_t) #error "Bad _TCHAR definition" #endif #if (_TEXT != L) #error "BAD _Text definiton" #endif // Timer to deal with double clicks and stuff like that. #define TRAY_ID 100 // timer ID to make sure the Tray icon appears! #define TIMER_ADD_TRAY 101 #define minOfFour(one,two,three,four) (min(min(one,two),min(three,four))) #define FILE_OPEN_THRESHOLD 16 #define CI_LOGON 1 #define CI_LOGOFF 2 // #define STWM_CSCCLOSEDIALOGS (WM_USER + 212) typedef HWND (WINAPI *CSCUIINITIALIZE)(HANDLE hToken, DWORD dwFlags); typedef LRESULT (WINAPI *CSCUISETSTATE)(UINT uMsg, WPARAM wParam, LPARAM lParam); #define REG_VALUE_NT_BUILD_NUMBER _TEXT("NTBuildNumber") #define REG_VALUE_DISABLE_AUTOCHECK _TEXT("DisableAutoCheck") #define REG_KEY_NETCACHE_SETTINGS _TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\NetCache") #define AGENT_ALIVE (vfCSCEnabled && !vfStopRecieved) #define AGENT_ALIVE_AND_ACTIVE (AGENT_ALIVE && !vfAgentQuiet) #define AGENT_ALIVE_AND_HAVE_NET (AGENT_ALIVE && vcntNetDevices) #define FIVE_MINUTES (5 * 1000 * 60) #define MAX_LIST_SIZE 1024 #define PI_LITELOAD 0x00000004 // Lite load of the profile (for system use only) // // Data declarations/definitions // #pragma data_seg(DATASEG_READONLY) _TCHAR vszKernel32[] = _TEXT("KERNEL32.DLL"); _TCHAR vszOpenVxDHandle[]=_TEXT("OpenVxDHandle"); static const _TCHAR szWkssvcToAgentStartEvent[] = _T("WkssvcToAgentStartEvent"); static const _TCHAR szWkssvcToAgentStopEvent[] = _T("WkssvcToAgentStopEvent"); static const _TCHAR szAgentToWkssvcEvent[] = _T("AgentToWkssvcEvent"); static const _TCHAR szAgentExistsEvent[] = _T("AgentExistsEvent"); static const _TCHAR vtzCSCUI[] = _TEXT("cscui.dll"); static const char vszCSCUIInitialize[] = "CSCUIInitialize"; static const char vszCSCUISetState[] = "CSCUISetState"; static const _TCHAR vtzDefaultExclusionList[] = L" *.SLM *.MDB *.LDB *.MDW *.MDE *.PST *.DB?"; DWORD vdwManualFileDetectionCount = 0; #pragma data_seg() #pragma data_seg(DATASEG_PERINSTANCE) static _TCHAR vrgchBuff[1024], vrwBuff[4096], vrgchSrcName[350], vrgchDstName[300]; static HMENU g_MainMenu; char vszDBDir[MAX_PATH]={0}; DWORD vdwDBCapacity = 0, vdwClusterSize = 0; DWORD vdwRedirStartTime = 0; AssertData; AssertError; #pragma data_seg() HWND vhwndMain = NULL; // main window BOOL vfAgentEnabledCSC=FALSE; // this is used to detect whether the remoteboot enabled CSC BOOL vfCSCEnabled=FALSE; // csc is enabled BOOL vfOKToEnableCSC = FALSE; HANDLE vhProfile = NULL; #pragma data_seg() BOOL vfFormatDatabase = FALSE; // set at init time #ifdef DEBUG ULONG ReintKdPrintVector = REINT_KDP_GOOD_DEFAULT; ULONG ReintKdPrintVectorDef = REINT_KDP_GOOD_DEFAULT; #endif unsigned ulFreePercent=30; // Amount of % cache freeing to be attempted // if the cache is full UINT vcntNetDevices = 0; // count of net devices BOOL g_bShowMergeIcon; // menu icon BOOL vfAgentRegistered = FALSE; BOOL vfClassRegistered = TRUE; BOOL vfMerging = FALSE; BOOL vfAgentQuiet = FALSE; DWORD vdwAgentThreadId = 0; DWORD vdwAgentSessionId = 0xffff; GLOBALSTATUS vsGS; BOOL allowAttempt; // set if we want to allow AttemptCacheFill now // // event handles of named events shared between usermode and kernel mode // HANDLE heventPerSess = NULL; HANDLE heventSharedFill = NULL; HANDLE vhMutex = NULL; DWORD dwVxDEvent = 0; // handle for VxD event obtained from heventShared HANDLE vhShadowDBForEvent = INVALID_HANDLE_VALUE; extern LPCONNECTINFO vlpLogonConnectList; extern _TCHAR * vrgchCRLF; HWND vhdlgShdLogon=NULL; // net start-stop vars HANDLE heventWkssvcToAgentStart = NULL;// event set by Workstation service on redir start HANDLE heventWkssvcToAgentStop = NULL; // event set by Workstation service on redir stop HANDLE heventAgentToWkssvc = NULL; // event used by agent to respond to wkssvc to tell it that // it is OK to stop the redir HANDLE heventShutDownAgent = NULL; HANDLE heventShutDownThread = NULL; HANDLE hCopyChunkThread = NULL; DWORD vdwCopyChunkThreadId = 0; BOOL vfRedirStarted = -1; BOOL vfStartRecieved = FALSE; BOOL vfStopRecieved = FALSE; BOOL fAgentShutDownRequested = FALSE; BOOL fAgentShutDown = FALSE; // CSCUI related HANDLE vhlibCSCUI = NULL; CSCUIINITIALIZE vlpfnCSCUIInitialize = NULL; CSCUISETSTATE vlpfnCSCUISetState = NULL; BOOL vfShowingOfflineDlg = FALSE; ULONG uOldDatabaseErrorFlags = 0; // // Function Prototypes // LRESULT CALLBACK ReInt_WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ); DWORD ReInt_AttemptCacheFill( LPVOID lpContext ); BOOL ReInt_RefreshTray( BOOL bHide ); BOOL ReInt_AnythingToMerge( VOID ); int InitMaint( VOID ); // Initialize the maintenance subsystem BOOL CheckCSCDatabaseVersion( BOOL *lpfWasDirty ); BOOL UpgradeCSCDatabase( LPSTR lpszDir ); BOOL IsNetConnected( VOID ); int ExtractSpaceStats( IN GLOBALSTATUS *lpsGS, OUT unsigned long *lpulMaxSpace, OUT unsigned long *lpulCurSpace, OUT unsigned long *lpulFreeSpace ); int InitCacheSize( VOID ); int SetDefaultSpace( LPSTR lpShadowDir ); BOOL NEAR PASCAL ReInt_InitApp( HANDLE hInstance ); BOOL NEAR PASCAL ReInt_InitInstance( HANDLE hInstance, HANDLE hPrevInstance, int cmdShow); BOOL NEAR PASCAL ReInt_TermInstance( VOID ); int DoEventProcessing( VOID ); BOOL FindCreateDBDir( BOOL *lpfCreated, BOOL fCleanup // empty the directory if found ); BOOL CreatePerSessSyncObjects( VOID ); BOOL CreateSharedFillSyncObjects( VOID ); BOOL EnableCSC( VOID ); BOOL DisableCSC( VOID ); BOOL IsCSCOn( VOID ); VOID ProcessStartStopAgent( VOID ); BOOL FStartAgent( VOID ); int StartStopCheck( VOID ); BOOL Reint_RegisterAgent( VOID ); VOID Reint_UnregisterAgent( VOID ); BOOL QueryEnableCSC( VOID ); VOID QueryMiscRegistryValues( VOID ); BOOL CreateStartStopEvents( VOID ); VOID DestroyStartStopEvents( VOID ); BOOL ProcessNetArrivalMessage( VOID ); BOOL ProcessNetDepartureMessage( BOOL fInvokeAutoDial ); BOOL WINAPI CheckCSC( LPSTR, BOOL ); BOOL ReportShareNetArrivalDeparture( BOOL fOneServer, HSHARE hShare, BOOL fInvokeAutoDial, BOOL fArrival ); LRESULT ReportEventsToSystray( DWORD dwMessage, WPARAM dwWParam, LPARAM dwLParam ); BOOL CheckServerOnline( VOID ); VOID SetAgentShutDown( VOID ); BOOL IsAgentShutDownRequested( VOID ); BOOL LaunchSystrayForLoggedonUser( VOID ); BOOL ImpersonateALoggedOnUser( VOID ); VOID ReportCreateDelete( HSHADOW hDir, HSHADOW hShadow, BOOL fCreated ); BOOL GetNameOfServerGoingOfflineEx( HANDLE hShadowDB, _TCHAR **lplptzServerName, DWORD *lpdwSize, BOOL *lpfAllocated ); BOOL AreAnyServersOffline( VOID); BOOL IsPersonal( VOID); BOOL IsMultipleUsersEnabled( void); BOOL IsFastUserSwitchingEnabled( VOID); BOOL AreConnectionsAllowed ( VOID); BOOL CanCSCLivewithTS( VOID); BOOL IsWorkstation( VOID); // // Functions // int PASCAL ReInt_WinMain( HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCommandLine, int cmdShow) /*++ Routine Description: This is the mainloop for the agent processing. It "schedules" different agent activities based on either an event set by the rdr or by predefined time intervals for these activities. The activities include a) Filling partially filled files b) checking for stale files c) maintaining space within limits d) reducing reference priority of all files for every FILE_OPEN_THRESHOLD fileopens --*/ { MSG msg; DWORD result; // result from Wait... BOOL done = FALSE; // to detect a quit message. BOOL staleInited = FALSE; // ensure that Staleness code runs DWORD timeToWait; DWORD nextGlobalStatusTime; DWORD newTick; DWORD nextSkipPurgeTime; // to figure out if we should perform an action. HANDLE hT[4]; if (hPrevInstance) { return FALSE; } vdwAgentThreadId = GetCurrentThreadId(); ReintKdPrint(MAINLOOP, ("Agent(1):----------ReInt_WinMain----------\n")); if (ReInt_InitApp(hInstance) && ReInt_InitInstance(hInstance, hPrevInstance, cmdShow)) { if (!AnyActiveNets(NULL)) vcntNetDevices = 1; newTick = GetTickCount(); nextGlobalStatusTime = newTick + WAIT_INTERVAL_GLOBALSTATUS_MS; nextSkipPurgeTime = newTick + WAIT_INTERVAL_SKIP_MS; hT[0] = heventPerSess; hT[1] = heventWkssvcToAgentStart; hT[2] = heventWkssvcToAgentStop; hT[3] = heventShutDownAgent; StartStopCheck(); ProcessStartStopAgent(); while (!done) { timeToWait = INFINITE; ReintKdPrint(MAINLOOP, ("Agent(1): Wait INFINITE\n")); result = WaitForMultipleObjects(4, hT, FALSE, timeToWait); newTick=GetTickCount(); if ((result == WAIT_OBJECT_0) || (result == (WAIT_OBJECT_0+4))) { ReintKdPrint(MAINLOOP, ("Agent(1):Event %d was fired or ReadGlobalStatus set\n", result)); DoEventProcessing(); // During event processing, we also do globalstatus check and // any other maintenance tasks. So let us restart the timer for // globalstatus nextGlobalStatusTime = newTick + WAIT_INTERVAL_GLOBALSTATUS_MS; } else if ((result == (WAIT_OBJECT_0+1)) || (result == (WAIT_OBJECT_0+2))) { ReintKdPrint(MAINLOOP, ("Agent(1): Received startstop \r\n")); vfStartRecieved = (result == (WAIT_OBJECT_0+1)); vfStopRecieved = (result == (WAIT_OBJECT_0+2)); ProcessStartStopAgent(); continue; } else if (result == (WAIT_OBJECT_0+3)) { ReintKdPrint(MAINLOOP, ("Agent(1):Agent ShutdownRequested, terminating agent\r\n")); SetAgentShutDown(); goto AllDone; } // do work only if CSC enabled if (vfCSCEnabled && AGENT_ALIVE_AND_ACTIVE) { // reset the staleness check time interval if(((int)(newTick - nextSkipPurgeTime)) >= 0) { // Unmark failures for servers that are known to be // disconnected and connection has not been attempted on them // for the last WAIT_INTERVAL_SKIP_MS milliseconds PurgeSkipQueue(FALSE, 0, 0, 0); vhcursor = NULL; nextSkipPurgeTime = newTick + WAIT_INTERVAL_SKIP_MS; ReintKdPrint(MAINLOOP, ("Agent(1):nextSkipPurgeTime = %d\n", nextSkipPurgeTime)); } if(((int)(newTick - nextGlobalStatusTime)) >= 0) { nextGlobalStatusTime = newTick + WAIT_INTERVAL_GLOBALSTATUS_MS; ReintKdPrint(MAINLOOP,("Agent(1):nextGlobalStatusTime = %d\n", nextGlobalStatusTime)); // We haven't gotten an event for sometime now from the // rdr, so let us go look what is up with him DoEventProcessing(); // reset the globalstatus time interval nextGlobalStatusTime = newTick + WAIT_INTERVAL_GLOBALSTATUS_MS; } } } } AllDone: // do termination processing ReintKdPrint(MAINLOOP, ("Agent(1):Exiting mainloop \r\n")); ReInt_TermInstance(); return 0; } BOOL NEAR PASCAL ReInt_InitApp( HANDLE hInstance ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { if ( CreateSharedFillSyncObjects() && CreatePerSessSyncObjects() ) { if (!CreateStartStopEvents()) { ReintKdPrint(BADERRORS, ("Agent:Failed to create Sync events \r\n")); return FALSE; } if (!(hCopyChunkThread = CreateThread( NULL, 8192, ReInt_AttemptCacheFill, NULL, 0, &vdwCopyChunkThreadId)) ) { ReintKdPrint(BADERRORS, ("Agent:Failed to create copychunk thread\r\n")); return FALSE; } return TRUE; } else { ReintKdPrint(BADERRORS, ("Failed to Create shared events\n")); return FALSE; } } BOOL NEAR PASCAL ReInt_InitInstance( HANDLE hInstance, HANDLE hPrevInstance, int cmdShow) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { BOOL fRet; fRet = InitValues(vszDBDir, sizeof(vszDBDir), &vdwDBCapacity, &vdwClusterSize); Assert(fRet); fAgentShutDown = FALSE; fAgentShutDownRequested = FALSE; if (!(vfOKToEnableCSC = QueryEnableCSC())) { ReintKdPrint(INIT, ("cscdll: Registry says disable CSC, not enabling\r\n")); } vfFormatDatabase = QueryFormatDatabase(); ReintKdPrint(INIT, ("Format=%d\n", vfFormatDatabase)); return (TRUE); } /*--------------------------------------------------------------------------*/ BOOL NEAR PASCAL ReInt_TermInstance( VOID ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { DisableCSC(); if (heventPerSess) { CloseHandle(heventPerSess); heventPerSess = NULL; } if (heventSharedFill) { CloseHandle(heventSharedFill); heventSharedFill = NULL; } PurgeSkipQueue(TRUE, 0, 0, 0); if (vhMutex) { ReleaseMutex(vhMutex); vhMutex = NULL; } // tell the workstation service that the agent is // going away if (heventAgentToWkssvc) { SetEvent(heventAgentToWkssvc); } if (heventShutDownThread) { DWORD dwRet; //Assert(hCopyChunkThread); SetEvent(heventShutDownThread); //If CopyChunk thread was created then cleanup - bug 562543 if (hCopyChunkThread) { dwRet = WaitForSingleObject(hCopyChunkThread, WAIT_INTERVAL_ATTEMPT_MS); ReintKdPrint(MAINLOOP, ("wait on thread handle %d \r\n", dwRet)); CloseHandle(hCopyChunkThread); } } DestroyStartStopEvents(); if (vfClassRegistered) { UnregisterClass(vszReintClass, vhinstCur); vfClassRegistered = FALSE; } return TRUE; } DWORD ReInt_AttemptCacheFill( LPVOID lpParams ) /*++ Routine Description: A wrapper for AttemptCacheFill. On NT many agent threads can do copychunk simultaneously, so there is no need to do mutual exclusion. --*/ { DWORD nextCheckServerOnlineTime; DWORD dwWaitResult; DWORD dwWaitResult2; DWORD dwWaitTime; DWORD newTick; ULONG nFiles = 0; ULONG nYoungFiles = 0; HANDLE hT[2]; DWORD dwManualFileDetectionCount = 0xffff; // on NT we run as a winlogon thread which has a very high process priority // so we have to get to the lowest if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST)) { ReintKdPrint(BADERRORS, ("RE: SetTheadPriority failed, reason: 0x%08x\n", GetLastError())); } ReintKdPrint(MAINLOOP, ("Agent(2): Launched.\n")); hT[0] = heventShutDownThread; hT[1] = heventSharedFill; nextCheckServerOnlineTime = GetTickCount() + WAIT_INTERVAL_CHECK_SERVER_ONLINE_MS + WAIT_INTERVAL_FILL_THROTTLE_MS; for (;;) { ReintKdPrint(MAINLOOP, ("Agent(2): nYoungFiles=%d\n", nYoungFiles)); if (nYoungFiles > 0 || AreAnyServersOffline() == TRUE) { dwWaitTime = WAIT_INTERVAL_CHECK_SERVER_ONLINE_MS; ReintKdPrint(MAINLOOP, ("Agent(2): Wait 8 min\n")); } else { dwWaitTime = INFINITE; ReintKdPrint(MAINLOOP, ("Agent(2): Wait INFINITE\n")); } dwWaitResult = WaitForMultipleObjects(2, hT, FALSE, dwWaitTime); if (dwWaitResult == (WAIT_OBJECT_0+0)) { // shutdown event ReintKdPrint(MAINLOOP, ("Agent(2): Termination event...\n")); if (AGENT_ALIVE && vdwAgentSessionId == 0) CSCPurgeUnpinnedFiles(100, &nFiles, &nYoungFiles); goto AllDone; } ReintKdPrint(MAINLOOP, ("Agent(2): Wait 2 min\n")); dwWaitResult2 = WaitForSingleObject(heventShutDownThread, WAIT_INTERVAL_FILL_THROTTLE_MS); if (dwWaitResult2 == (WAIT_OBJECT_0+0)) { // shutdown event ReintKdPrint(MAINLOOP, ("Agent(2): Termination event...\n")); if (AGENT_ALIVE && vdwAgentSessionId == 0) CSCPurgeUnpinnedFiles(100, &nFiles, &nYoungFiles); goto AllDone; } if (dwWaitResult == WAIT_TIMEOUT) { // timeout if (AGENT_ALIVE) { ReintKdPrint(MAINLOOP, ("Agent(2): Timeout...\n")); AttemptCacheFill(0, DO_ONE_OBJECT, FALSE, CSC_INVALID_PRINCIPAL_ID, NULL, 0); if (vdwAgentSessionId == 0) { GetManualFileDetectionCounter(INVALID_HANDLE_VALUE,&dwManualFileDetectionCount); vdwManualFileDetectionCount = dwManualFileDetectionCount; CSCPurgeUnpinnedFiles(100, &nFiles, &nYoungFiles); } } } else if (dwWaitResult == (WAIT_OBJECT_0+1)) { // kernel told us to run if (AGENT_ALIVE) { ReintKdPrint(MAINLOOP, ("Agent(2): Shared Event signal...\n")); AttemptCacheFill(0, DO_ONE_OBJECT, FALSE, CSC_INVALID_PRINCIPAL_ID, NULL, 0); GetManualFileDetectionCounter(INVALID_HANDLE_VALUE,&dwManualFileDetectionCount); if (dwManualFileDetectionCount != vdwManualFileDetectionCount) { vdwManualFileDetectionCount = dwManualFileDetectionCount; CSCPurgeUnpinnedFiles(100, &nFiles, &nYoungFiles); } } } newTick = GetTickCount(); if(((int)(newTick - nextCheckServerOnlineTime)) >= 0) { if (AGENT_ALIVE) { // check whether one or more shares that are presently in // disconnected state have come online. // If they are, report them to the UI CheckServerOnline(); nextCheckServerOnlineTime = newTick + WAIT_INTERVAL_CHECK_SERVER_ONLINE_MS + WAIT_INTERVAL_FILL_THROTTLE_MS; } } } AllDone: ReintKdPrint(MAINLOOP, ("Agent(2):Thread exit\n")); return 0; } VOID ReInt_DoFreeShadowSpace( GLOBALSTATUS *lpsGS, int fForce ) /*++ Routine Description: The function is called from the main loop every "n" minutes to see if we are running out of space. If so, it tries to free up some percentage of the shadow cache. Parameters: fForce: 0 => do it only if we don't have space and we are on the net 1 => do it if we are on the net 2 => just do it Return Value: Notes: --*/ { ULONG ulMax; ULONG ulCur; ULONG ulFree; WIN32_FIND_DATA sFind32; LPCOPYPARAMS lpCP = NULL; if (!lpsGS) { lpsGS = &vsGS; } ReintKdPrint(MERGE, ("ReInt_DoFreeShadowSpace(1)\r\n")); // Get space stats if (ExtractSpaceStats(lpsGS, &ulMax, &ulCur, &ulFree) >= 0){ ReintKdPrint(MERGE, ("ReInt_DoFreeShadowSpace(2)\r\n")); // do we have space and are not forced to free up? if ((fForce < 1) && (ulFree > 0)){ ReintKdPrint(MERGE, ("ReInt_DoFreeShadowSpace(3)\r\n")); return; } // // We are forced or there is no space // // NB!!!! We check for a net device and if it exists we assume that // it is OK to freespace on all shares. if ((fForce < 2) && !vcntNetDevices){ ReintKdPrint(MERGE, ("ReInt_DoFreeShadowSpace: No net, aborting \r\n")); return; } // Maximum force, or all conditions for freeing are being met // ie. there is no space and we are on the net memset(&sFind32, 0, sizeof(sFind32)); // NB the math below is done to avoid overflow. // The consequence is that the resulting value is less than // the percentage of cache space to be freed. ulFree = (ulMax/100) * ulFreePercent; // if the cached data is more than the earmarked space // then add the extra too. if (ulCur > ulMax) { ulFree += (ulCur - ulMax); } DosToWin32FileSize(ulFree, &sFind32.nFileSizeHigh, &sFind32.nFileSizeLow); ReintKdPrint(MERGE, ("ReInt_DoFreeShadowSpace(): freeing %d\n", ulFree)); ReintKdPrint(MERGE, (" nFileSizeLow=%d\n", sFind32.nFileSizeLow)); FreeShadowSpace(INVALID_HANDLE_VALUE, sFind32.nFileSizeHigh, sFind32.nFileSizeLow, FALSE); // don't clear all ReintKdPrint(MERGE, ("ReInt_DoFreeShadowSpace(): ending.\n")); } } /*********************** Merging related routines **************************/ // // DoubleClick/Menu handler. // VOID ReInt_DoNetProp( VOID ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { HINSTANCE hLib=LoadLibrary(_TEXT("shhndl.dll")); ReintKdPrint(BADERRORS, ("LoadLibrary of shhndl returned %d\n",hLib)); if(hLib) { FARPROC lpFn=GetProcAddress(hLib,"NetProp_Create"); ReintKdPrint(BADERRORS, ("NetProp_Create is 0x%x\n",lpFn)); if(lpFn) lpFn(); FreeLibrary(hLib); } } // // Command Handler // BOOL NEAR PASCAL ReInt_CommandHandler( HWND hwnd, WPARAM wParam, LPARAM lParam ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { unsigned long ulSwitch, ulSav; switch (wParam) { case IDM_PROPERTIES: ReInt_DoNetProp(); break; case IDM_SHADOW_LOG: { if(ShadowSwitches(INVALID_HANDLE_VALUE, &ulSwitch, SHADOW_SWITCH_GET_STATE)) { ulSav = (ulSwitch & SHADOW_SWITCH_LOGGING); ulSwitch = SHADOW_SWITCH_LOGGING; if (ShadowSwitches(INVALID_HANDLE_VALUE, &ulSwitch, (ulSav)?SHADOW_SWITCH_OFF:SHADOW_SWITCH_ON)) CheckMenuItem(GetMenu(hwnd), IDM_SHADOW_LOG , MF_BYCOMMAND|((ulSav)?MF_UNCHECKED:MF_CHECKED)); } } break; case IDM_LOG_COPYING: { #ifdef TEST HKEY hKey=0; _TCHAR szDoCopy[MAX_NAME_LEN]; vfLogCopying = vfLogCopying?0:1; CheckMenuItem(GetMenu(hwnd), IDM_LOG_COPYING , MF_BYCOMMAND|((vfLogCopying)?MF_UNCHECKED:MF_CHECKED)); if(RegOpenKey(HKEY_LOCAL_MACHINE, REG_KEY_CSC_SETTINGS, &hKey) != ERROR_SUCCESS) { ReintKdPrint(BADERRORS, ("IDM_LOG_COPYING: RegOpenKey failed\n")); goto done; } if(vfLogCopying) strcpy(szDoCopy, SZ_TRUE); else strcpy(szDoCopy, SZ_FALSE); if(RegSetValueEx(hKey, vszDoLogCopy, (DWORD) 0, REG_SZ, szDoCopy, strlen(szDoCopy)+1) != ERROR_SUCCESS) { ReintKdPrint(BADERRORS, ("IDM_LOG_COPYING: RegSetValueEx failed\n")); } done: if(hKey) RegCloseKey(hKey); #endif //TEST } break; case IDM_SHADOWING: break; case IDM_SPEED_OPT: break; case IDM_TRAY_FILL_SHADOW: break; case IDM_TRAY_MERGE: break; case IDM_TRAY_FREE_SPACE: break; case IDM_TRAY_FORCE_LOG: break; case IDM_REFRESH_CONNECTIONS: break; case IDM_BREAK_CONNECTIONS: break; case IDM_LOGON: if (lParam) { vfStartRecieved = TRUE; // logon is done, try enabling CSC // if CSC is already enabled, the routine will do the right thing // EnableCSC(); } break; case IDM_LOGOFF: // no need to trap this, we get WM_QUERYENDSESSION and WM_ENDSESSION break; default: return FALSE; } return TRUE; } LRESULT CALLBACK ReInt_WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { LPARAM lRet = 0L; switch (message) { case RWM_UPDATE: ReintKdPrint(BADERRORS, ("Update hShare:%0x hWnd=%0x\n",wParam, lParam)); PurgeSkipQueue(TRUE, (HSHARE)wParam, 0, 0); // return ReintOneShare((HSHARE)wParam,(HWND)lParam); break; case RWM_UPDATEALL: break; case WM_TIMER: break; case TRAY_NOTIFY: break; case WM_DEVICECHANGE: { switch (wParam) { case DBT_DEVICEARRIVAL: if (((DEV_BROADCAST_NET *)lParam)->dbcn_devicetype == DBT_DEVTYP_NET) { // ProcessNetArrivalMessage(); } break; case DBT_DEVICEREMOVECOMPLETE: if (((DEV_BROADCAST_NET *)lParam)->dbcn_devicetype == DBT_DEVTYP_NET) { // ProcessNetDepartureMessage(FALSE); } break; } break; } case WM_INITMENU: return TRUE; break; case WM_INITMENUPOPUP: break; case WM_COMMAND: if(lRet = ReInt_CommandHandler(hwnd, wParam, lParam)) return lRet; break; case WM_CLOSE: ReintKdPrint(BADERRORS, ("WM_CLOSE hit.\n")); break; case WM_DESTROY: ReintKdPrint(BADERRORS, ("WM_DESTROY hit.\n")); PostQuitMessage((int)wParam); return 1L; case WM_SETCURSOR: break; case WM_QUERYENDSESSION: return TRUE; case WM_ENDSESSION: ReintKdPrint(BADERRORS, ("Turning off shadowing on WM_ENDSESSION\r\n")); DisableCSC(); break; case WM_FILE_OPENS: break; case WM_SHADOW_ADDED: case WM_SHADOW_DELETED: ReintKdPrint(BADERRORS, ("allowAttempt = TRUE\n")); allowAttempt = TRUE; break; case WM_SHARE_DISCONNECTED: ReintKdPrint(BADERRORS, ("REINT: VxD notification(0x%08x)\n",message)); break; default: break; } return DefWindowProc(hwnd, message, wParam, lParam); } BOOL ReInt_AnythingToMerge( VOID ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { return (CheckDirtyShares() != 0); } // // BOOL ReInt_RefreshTray( BOOL bHide ) /*++ Routine Description: Called to update the tray ICON to reflect the merge status. Parameters: bHide = TRUE means hide it = FALSE means work it out. Return Value: Notes: --*/ { return TRUE; } // See if shadfowing is ON // Returns: TRUE=> ON, FALSE=> OFF, -1 => soem error happened BOOL IsCSCOn( VOID ) { if (vfCSCEnabled) { #ifdef DEBUG unsigned ulSwitch = SHADOW_SWITCH_SHADOWING; if(ShadowSwitches(INVALID_HANDLE_VALUE, &ulSwitch, SHADOW_SWITCH_GET_STATE)) { Assert((ulSwitch & SHADOW_SWITCH_SHADOWING)!=0); } #endif return (TRUE); } return (FALSE); } // Disable Shadowing // Returns: 1 => done, -1 => some error happened int DisableCSC() { unsigned ulSwitch = SHADOW_SWITCH_SHADOWING; if (vfCSCEnabled && vfAgentEnabledCSC) { if(ShadowSwitches(INVALID_HANDLE_VALUE, &ulSwitch, SHADOW_SWITCH_OFF)) { vfCSCEnabled = FALSE; if (vhShadowDBForEvent != INVALID_HANDLE_VALUE) { CloseHandle(vhShadowDBForEvent); vhShadowDBForEvent = INVALID_HANDLE_VALUE; } Reint_UnregisterAgent(); // SetDisabledReg(); return (1); } } return (-1); } BOOL EnableCSC( VOID ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { char szBuff[MAX_PATH]; DWORD dwBuffSize; BOOL fDirCreated=FALSE, fRedirCSCEnabled=TRUE, fWasDirty = FALSE; unsigned uShadowSwitches; ReintKdPrint(INIT, ("CSC Enabled %d \r\n", vfCSCEnabled)); if (vfCSCEnabled==FALSE) { dwBuffSize = sizeof(szBuff); if(!GetUserNameA(szBuff, &dwBuffSize)) { // not logged on yet return FALSE; } if(ShadowSwitches(INVALID_HANDLE_VALUE, &uShadowSwitches, SHADOW_SWITCH_GET_STATE)) { if (uShadowSwitches & SHADOW_SWITCH_SHADOWING) { ReintKdPrint(INIT, ("cscdll: CSC already started\r\n")); } else { ReintKdPrint(INIT, ("cscdll: redir is not doing CSC yet, OK\r\n")); fRedirCSCEnabled = FALSE; if (!vfOKToEnableCSC) { return FALSE; } vfAgentEnabledCSC = TRUE; } } else { ReintKdPrint(BADERRORS, ("cscdll: couldn't get the CSC state from the redir\r\n")); } if (Reint_RegisterAgent()) { SetFileAttributesA(vszDBDir, FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN); ReintKdPrint(INIT, ("cscdll: enabling CSC at %s for %s with capacity %d and clustersize %d\r\n", vszDBDir, szBuff, vdwDBCapacity, vdwClusterSize)); if (EnableShadowing( INVALID_HANDLE_VALUE, vszDBDir, szBuff, 0, vdwDBCapacity, vdwClusterSize, vfFormatDatabase)) { vfCSCEnabled = TRUE; if (vhShadowDBForEvent == INVALID_HANDLE_VALUE) { vhShadowDBForEvent = OpenShadowDatabaseIO(); } } else { ReintKdPrint(BADERRORS, ("cscdll: EnableShadowing failed, CSC not enabled!!!!\r\n")); } if (vfCSCEnabled==FALSE) { Reint_UnregisterAgent(); } } else { ReintKdPrint(BADERRORS, ("cscdll: EnableCSC.Agent registration failed, CSC not enabled!!!!\r\n")); } } if (!vfCSCEnabled) { // NTRAID-455253-1/31/2000-shishirp need to add it to the event log with the right error ReintKdPrint(BADERRORS, ("cscdll: CSC not enabled \r\n")); } return (vfCSCEnabled); } BOOL IsNetConnected( VOID ) /*++ Routine Description: The function checks to see whether at this moment we are connected to any resources on the real net. Parameters: Return Value: Notes: This is used to decide whether to start purging stuff from the cache. If we are in a completely disconnected state then we may not want to purge data that is potentially useful. --*/ { CSC_ENUMCOOKIE ulEnumCookie=NULL; WIN32_FIND_DATA sFind32; BOOL fConnected = FALSE; SHADOWINFO sSI; HANDLE hShadowDB; memset(&sFind32, 0, sizeof(sFind32)); lstrcpy(sFind32.cFileName, _TEXT("*.*")); if ((hShadowDB = OpenShadowDatabaseIO()) == INVALID_HANDLE_VALUE) { return FALSE; } if(FindOpenShadow(hShadowDB, 0, FINDOPEN_SHADOWINFO_ALL, &sFind32, &sSI)) { ulEnumCookie = sSI.uEnumCookie; do { if (sSI.uStatus & SHARE_CONNECTED) { if (!(sSI.uStatus & SHARE_DISCONNECTED_OP)) { fConnected = TRUE; break; } } } while(FindNextShadow(hShadowDB, ulEnumCookie, &sFind32, &sSI)); FindCloseShadow(hShadowDB, ulEnumCookie); } CloseShadowDatabaseIO(hShadowDB); return fConnected; } int ExtractSpaceStats( GLOBALSTATUS *lpsGS, unsigned long *lpulMaxSpace, unsigned long *lpulCurSpace, unsigned long *lpulFreeSpace ) /*++ Routine Description: The function returns the max, current and free space as known by the shadow cache. Parameters: Return Value: 1 if there is any free space 0 if there is no free space -1 if there is some error Notes: --*/ { int iRet = 1; if (!lpsGS) { lpsGS = &vsGS; } if (lpulMaxSpace){ *lpulMaxSpace = lpsGS->sST.sMax.ulSize; } if (lpulCurSpace){ *lpulCurSpace = lpsGS->sST.sCur.ulSize; } if (lpulFreeSpace){ *lpulFreeSpace = 0; } // do we have any space? if (lpsGS->sST.sMax.ulSize > lpsGS->sST.sCur.ulSize){ if (lpulFreeSpace){ *lpulFreeSpace = (lpsGS->sST.sMax.ulSize - lpsGS->sST.sCur.ulSize); } iRet = 1; } else{ iRet = 0; } return iRet; } int InitCacheSize( VOID ) /*++ Routine Description: The function returns the max, current and free space as known by the shadow cache. Parameters: Return Value: 1 if there is any free space 0 if there is no free space -1 if there is some error Notes: --*/ { unsigned ulMaxStore; int iRet = 0; if(!GetGlobalStatus(INVALID_HANDLE_VALUE, &vsGS)) { return -1; } if (ExtractSpaceStats(&vsGS, &ulMaxStore, NULL, NULL)>=0){ if (ulMaxStore==0xffffffff){ ReintKdPrint(BADERRORS, ("Agent: Found newly created cache, setting cache size \r\n")); Assert(vszDBDir[0]); iRet = SetDefaultSpace(vszDBDir); } } return iRet; } int SetDefaultSpace( LPSTR lpShadowDir ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { DWORD dwSPC, dwBPS, dwFreeC, dwTotalC, dwCapacity; _TCHAR szDrive[4]; int iRet = 0; Assert(lpShadowDir[1]==':'); memset(szDrive, 0, sizeof(szDrive)); memcpy(szDrive, lpShadowDir, 3); if(GetDiskFreeSpace(szDrive, &dwSPC, &dwBPS, &dwFreeC, &dwTotalC )){ dwCapacity = ((dwSPC * dwBPS * 10)/100)*dwTotalC; SetMaxShadowSpace(INVALID_HANDLE_VALUE, 0, dwCapacity); iRet = 1; } return (iRet); } int DoEventProcessing( VOID ) /*++ Routine Description: When the named event is triggered by the kernel mode component, this routine looks to see what needs to be taken care of and does the job. Parameters: Return Value: Notes: This routine is called from various places in the mailoop and other loops such as AttemptCacheFill. It may end up showing up dialog boxes and such, so care has to be taken while invoking it. --*/ { int iRet; GLOBALSTATUS sGS; ReintKdPrint( INIT, ("CSC Agent: CSC Enabled=%d vhShadowDBForEvent \n", vfCSCEnabled, vhShadowDBForEvent)); ReintKdPrint(MAINLOOP, ("Agent(1):DoEventProcessing()\n")); if (iRet = GetGlobalStatus(vhShadowDBForEvent, &sGS)) { ReintKdPrint(MAINLOOP, ( "Agent(1):uFlagsEvents:0x%x\n" " uDatabaseErrorFlags:0x%x\n" " hShadowAdded:0x%x\n" " hDirAdded:0x%x\n" " hShadowDeleted:0x%x\n" " hDirDeleted:0x%x\n" " cntFileOpen:%d\n" " hShareDisconnected:0x%x\n", sGS.uFlagsEvents, sGS.uDatabaseErrorFlags, sGS.hShadowAdded, sGS.hDirAdded, sGS.hShadowDeleted, sGS.hDirDeleted, sGS.cntFileOpen, sGS.hShareDisconnected, sGS.uFlagsEvents)); if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_START) ReintKdPrint(MAINLOOP, ("Agent(1):FLAG_GLOBALSTATUS_START received\r\n")); if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_STOP) ReintKdPrint(MAINLOOP, ("Agent(1):FLAG_GLOBALSTATUS_STOP received\r\n")); if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_NO_NET) ReintKdPrint(MAINLOOP, ("Agent(1):FLAG_GLOBALSTATUS_NO_NET received\r\n")); if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_GOT_NET) ReintKdPrint(MAINLOOP, ("Agent(1):FLAG_GLOBALSTATUS_GOT_NET received\r\n")); if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_INVOKE_FREESPACE) ReintKdPrint(MAINLOOP, ("Agent(1):FLAG_GLOBALSTATUS_INVOKE_FREESPACE received\r\n")); if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_SHARE_DISCONNECTED) ReintKdPrint( MAINLOOP, ("Agent(1):FLAG_GLOBALSTATUS_SHARE_DISCONNECTED (share=%d) received\r\n", sGS.hShareDisconnected)); if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_START) { vfCSCEnabled = TRUE; if (vhShadowDBForEvent == INVALID_HANDLE_VALUE) { vhShadowDBForEvent = OpenShadowDatabaseIO(); } Reint_RegisterAgent(); } else if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_STOP) { vfCSCEnabled = FALSE; if (vhShadowDBForEvent != INVALID_HANDLE_VALUE) { CloseHandle(vhShadowDBForEvent); vhShadowDBForEvent = INVALID_HANDLE_VALUE; } Reint_UnregisterAgent(); } if (AGENT_ALIVE) { if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_GOT_NET) { ProcessNetArrivalMessage(); } else if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_NO_NET) { ProcessNetDepartureMessage( ((sGS.uFlagsEvents & FLAG_GLOBALSTATUS_INVOKE_AUTODIAL)!=0)); } if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_SHARE_DISCONNECTED) { ReportShareNetArrivalDeparture( 1, sGS.hShareDisconnected, ((sGS.uFlagsEvents & FLAG_GLOBALSTATUS_INVOKE_AUTODIAL)!=0), FALSE); // departed } if (uOldDatabaseErrorFlags != sGS.uDatabaseErrorFlags) { ReportEventsToSystray(STWM_CACHE_CORRUPTED, 0, 0); uOldDatabaseErrorFlags = sGS.uDatabaseErrorFlags; } // see if space needs freeing if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_INVOKE_FREESPACE) { ReintKdPrint(MAINLOOP, ("Agent(1): Calling DoFreeShadowSpace(1)\r\n")); ReInt_DoFreeShadowSpace(&sGS, 0); } else { ReintKdPrint(MAINLOOP, ("Agent(1): Calling DoFreeShadowSpace(2)\r\n")); ReInt_DoFreeShadowSpace(&sGS, 0); } } else { if (sGS.uFlagsEvents & FLAG_GLOBALSTATUS_SHARE_DISCONNECTED) { ReintKdPrint(MAINLOOP, ("Agent(1): Calling ReportShareNetArrivalDeparture\r\n")); ReportShareNetArrivalDeparture( 1, 0, ((sGS.uFlagsEvents & FLAG_GLOBALSTATUS_INVOKE_AUTODIAL)!=0), FALSE); // departed } } vsGS = sGS; vsGS.uFlagsEvents = 0; // clear all event indicators } return iRet; } BOOL CreatePerSessSyncObjects( VOID ) { NTSTATUS Status; UNICODE_STRING EventName; OBJECT_ATTRIBUTES ObjectAttributes; WCHAR SessEventName[100]; // DbgPrint("CreatePerSessSyncObjects:vdwAgentSessionId = %d\n", vdwAgentSessionId); Assert(heventPerSess == NULL); wsprintf(SessEventName, L"%ws_%d", SESSION_EVENT_NAME_NT, vdwAgentSessionId); // DbgPrint("CreatePerSessSyncObjects:SessEventName = [%ws]\n", SessEventName); RtlInitUnicodeString(&EventName, SessEventName); InitializeObjectAttributes( &ObjectAttributes, &EventName, OBJ_OPENIF, //got this const from base\client\support.c (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); Status = NtCreateEvent( &heventPerSess, EVENT_ALL_ACCESS, &ObjectAttributes, SynchronizationEvent, FALSE ); if (!NT_SUCCESS(Status)) { DbgPrint("CreatePerSessSyncObjects:NtCreateEvent returned %08lx\n",Status); } return (heventPerSess==0)?FALSE:TRUE; } BOOL CreateSharedFillSyncObjects( VOID ) { NTSTATUS Status; UNICODE_STRING EventName; OBJECT_ATTRIBUTES ObjectAttributes; Assert(heventSharedFill == NULL); RtlInitUnicodeString(&EventName,SHARED_FILL_EVENT_NAME_NT); InitializeObjectAttributes( &ObjectAttributes, &EventName, OBJ_OPENIF, //got this const from base\client\support.c (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); Status = NtCreateEvent( &heventSharedFill, EVENT_ALL_ACCESS, &ObjectAttributes, SynchronizationEvent, FALSE ); if (!NT_SUCCESS(Status)) { DbgPrint("ntcreateeventstatus=%08lx\n",Status); } return (heventSharedFill==0)?FALSE:TRUE; } VOID ProcessStartStopAgent( VOID ) { if (vfStartRecieved) { ReintKdPrint(MAINLOOP, ("Agent(1): start received, enabling CSC\r\n")); if (EnableCSC() == FALSE) { ReintKdPrint(ALWAYS, ("Ageint(1):Couldn't turn CSC ON!!!!!!!!! \n")); } else { UpdateExclusionList(); UpdateBandwidthConservationList(); } // set the event to indicate to wkssvc that we are alive if (heventAgentToWkssvc) { SetEvent(heventAgentToWkssvc); } vfStartRecieved = FALSE; vfRedirStarted = TRUE; } else if (vfStopRecieved) { ReintKdPrint(MAINLOOP, ("Agent(1):Stop recieved \r\n")); DisableCSC(); if(heventAgentToWkssvc) { SetEvent(heventAgentToWkssvc); } vcntNetDevices = 0; vfStopRecieved = FALSE; vfRedirStarted = FALSE; } } BOOL FStopAgent( VOID ) { if (StartStopCheck() && vfStopRecieved) { return TRUE; } return FALSE; } BOOL StartStopCheck( VOID ) { DWORD dwError; BOOL fRet = FALSE; HANDLE hT[2]; unsigned ulSwitch=0; // the way we are finding out about the start makes the start event // redundant, but we will leave it, because it is generically the right thing to have a // start event and a stop event. if ((vfRedirStarted == -1) && // if newly launched agent (ShadowSwitches(INVALID_HANDLE_VALUE, &ulSwitch, SHADOW_SWITCH_GET_STATE))) { ReintKdPrint(INIT, ("Agent: redir already started\r\n")); vfStartRecieved = TRUE; } else if (heventWkssvcToAgentStart) { // we know that when it comes to events it is all or nothing Assert(heventWkssvcToAgentStop); hT[0] = heventWkssvcToAgentStart; hT[1] = heventWkssvcToAgentStop; dwError = MsgWaitForMultipleObjects(2, hT, FALSE, 0, QS_ALLINPUT); if (vfRedirStarted == TRUE) { vfStopRecieved = (dwError == WAIT_OBJECT_0+1); if (vfStopRecieved) { ReintKdPrint(INIT, ("Agent: stop recieved\r\n")); } } else { vfStartRecieved = (dwError == WAIT_OBJECT_0); if (vfStartRecieved) { ReintKdPrint(INIT, ("Agent: start recieved\r\n")); } } } return fRet; } BOOL Reint_RegisterAgent( VOID ) { if (!vfAgentRegistered) { if (!RegisterAgent(INVALID_HANDLE_VALUE, vhwndMain, LongToHandle(dwVxDEvent))) { ReintKdPrint(BADERRORS, ("Agent registration failed \n")); return FALSE; } else { vfAgentRegistered = TRUE; } } return vfAgentRegistered; } VOID Reint_UnregisterAgent( VOID ) { if (vfAgentRegistered) { // don't do any checking UnregisterAgent(INVALID_HANDLE_VALUE, vhwndMain); vfAgentRegistered = FALSE; } } BOOL QueryEnableCSC( VOID ) { DWORD dwDisposition, dwSize, dwEnabled=0; HKEY hKey = NULL; BOOL fRet = TRUE; int i; _TCHAR *lpKey; NT_PRODUCT_TYPE productType; if( !RtlGetNtProductType( &productType ) ) { productType = NtProductWinNt; } switch ( productType ) { case NtProductWinNt: /* WORKSTATION */ ReintKdPrint(INIT, ("Agent:CSC running workstation\r\n")); break; default: ReintKdPrint(INIT, ("Agent:CSC NOT running workstation\r\n")); fRet = FALSE; // default is fail } //Check if CSC can work with current settings for TS //Test for TS added by Navjot as per Bug#468391 if (IsPersonal() == TRUE || !CanCSCLivewithTS() ) return FALSE; for (i=0; i<2; ++i) { if (i==0) { lpKey = REG_STRING_POLICY_NETCACHE_KEY; } else { lpKey = REG_STRING_NETCACHE_KEY; } if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, lpKey, 0, KEY_READ | KEY_WRITE, &hKey ) == ERROR_SUCCESS) { dwSize = sizeof(dwEnabled); if (RegQueryValueEx(hKey, REG_VALUE_ENABLED, NULL, NULL, (void *)&dwEnabled, &dwSize) == ERROR_SUCCESS) { fRet = (dwEnabled != 0); break; } RegCloseKey(hKey); hKey = NULL; } else { hKey = NULL; } } if(hKey) { RegCloseKey(hKey); } return (fRet); } BOOL CreateStartStopEvents( VOID ) { BOOL fOK = FALSE; // ensure that there are thre named autoreset events if (!heventWkssvcToAgentStart) { heventWkssvcToAgentStart = CreateEvent(NULL, FALSE, FALSE, szWkssvcToAgentStartEvent); if (!heventWkssvcToAgentStart) { ReintKdPrint(BADERRORS, ("CSC.Agent: Failed to create heventWkssvcToAgentStart, error = %d\n", GetLastError())); goto bailout; } Assert(!heventAgentToWkssvc); heventWkssvcToAgentStop = CreateEvent(NULL, FALSE, FALSE, szWkssvcToAgentStopEvent); if (!heventWkssvcToAgentStop) { ReintKdPrint(BADERRORS, ("CSC.Agent: Failed to create heventWkssvcToAgentStop, error = %d\n", GetLastError())); goto bailout; } heventAgentToWkssvc = CreateEvent(NULL, FALSE, FALSE, szAgentToWkssvcEvent); if (!heventAgentToWkssvc) { ReintKdPrint(BADERRORS, ("CSC.Agent: Failed to create heventAgentToWkssvc, error = %d\n", GetLastError())); goto bailout; } // event to detect whether the agent is alive (used by wkssvc) and // to signal termination (used by winlogon) heventShutDownAgent = CreateEvent(NULL, FALSE, FALSE, szAgentExistsEvent); if (!heventShutDownAgent) { ReintKdPrint(BADERRORS, ("CSC.Agent: Failed to create heventShutDownAgent, error = %d\n", GetLastError())); goto bailout; } heventShutDownThread = CreateEvent(NULL, FALSE, FALSE, NULL); if (!heventShutDownThread) { ReintKdPrint(BADERRORS, ("CSC.Agent: Failed to create heventShutDownThread, error = %d\n", GetLastError())); goto bailout; } } fOK = TRUE; bailout: if (!fOK) { DestroyStartStopEvents(); } return fOK; } VOID DestroyStartStopEvents( VOID ) { if (heventWkssvcToAgentStart) { CloseHandle(heventWkssvcToAgentStart); heventWkssvcToAgentStart = NULL; } if (heventWkssvcToAgentStop) { CloseHandle(heventWkssvcToAgentStop); heventWkssvcToAgentStop = NULL; } if (heventAgentToWkssvc) { CloseHandle(heventAgentToWkssvc); heventAgentToWkssvc = NULL; } if (heventShutDownAgent) { CloseHandle(heventShutDownAgent); heventShutDownAgent = NULL; } if (heventShutDownThread) { CloseHandle(heventShutDownThread); heventShutDownThread = NULL; } } BOOL UpdateExclusionList( VOID ) /*++ Routine Description: Tell the kernel mode code about the exclusion list. If there is none set in the registry then we set the default one Parameters: Return Value: Notes: --*/ { LPWSTR lpwExclusionList = NULL; DWORD cbSize = 0; char buff[MAX_LIST_SIZE]; // max exclusion list BOOL fRet = FALSE; if (!vfCSCEnabled) { ReintKdPrint(INIT, ("CSC not enabled \r\n")); return FALSE; } ReintKdPrint(INIT, ("Getting ExclusionList \r\n")); // get the exclusion list from the policy key. // if that doesn't work then try the one from the netcache key if (GetWideStringFromRegistryString(REG_STRING_POLICY_NETCACHE_KEY_A, REG_STRING_EXCLUSION_LIST_A, &lpwExclusionList, &cbSize) || GetWideStringFromRegistryString(REG_STRING_NETCACHE_KEY_A, REG_STRING_EXCLUSION_LIST_A, &lpwExclusionList, &cbSize) ) { ReintKdPrint(INIT, ("Got ExclusionList \r\n")); if (cbSize < sizeof(buff)) { memcpy(buff, lpwExclusionList, cbSize); ReintKdPrint(INIT, ("Setting User defined exclusion list %ls size=%d\r\n", buff, cbSize)); if (SetExclusionList(INVALID_HANDLE_VALUE, (USHORT *)buff, cbSize)) { fRet = TRUE; } } } else { // set the default // take the string and it's terminating null char cbSize = sizeof(vtzDefaultExclusionList); Assert(cbSize < MAX_LIST_SIZE); memcpy(buff, vtzDefaultExclusionList, cbSize); ReintKdPrint(INIT, ("Setting default exclusion list %ls size=%d\r\n", buff, cbSize)); if (SetExclusionList(INVALID_HANDLE_VALUE, (USHORT *)buff, cbSize)) { fRet = TRUE; } } if (lpwExclusionList) { LocalFree(lpwExclusionList); } return fRet; } BOOL UpdateBandwidthConservationList( VOID ) /*++ Routine Description: Update the list of extensions on which bitcopy should be turned ON. We do not set any default Parameters: Return Value: Notes: --*/ { LPWSTR lpwBandwidthConservationList = NULL; DWORD cbSize = 0; char buff[MAX_LIST_SIZE]; // max exclusion list BOOL fRet = FALSE; if (!vfCSCEnabled) { ReintKdPrint(INIT, ("CSC not enabled \r\n")); return FALSE; } ReintKdPrint(INIT, ("Getting BandwidthConservationList \r\n")); // get the exclusion list from the policy key. // if that doesn't work then try the one from the netcache key if (GetWideStringFromRegistryString(REG_STRING_POLICY_NETCACHE_KEY_A, REG_STRING_BANDWIDTH_CONSERVATION_LIST_A, &lpwBandwidthConservationList, &cbSize) || GetWideStringFromRegistryString(REG_STRING_NETCACHE_KEY_A, REG_STRING_BANDWIDTH_CONSERVATION_LIST_A, &lpwBandwidthConservationList, &cbSize) ) { ReintKdPrint(INIT, ("Got BandwidthConservationList \r\n")); if (cbSize < sizeof(buff)) { memcpy(buff, lpwBandwidthConservationList, cbSize); ReintKdPrint(INIT, ("Setting User defined bandwidth conservation list %ls size=%d\r\n", buff, cbSize)); if (SetBandwidthConservationList(INVALID_HANDLE_VALUE, (USHORT *)buff, cbSize)) { fRet = TRUE; } } } else { fRet = FALSE; #if 0 // set the default // take the string and it's terminating null char cbSize = sizeof(vtzDefaultBandwidthConservationList); Assert(cbSize < MAX_LIST_SIZE); memcpy(buff, vtzDefaultBandwidthConservationList, cbSize); ReintKdPrint(INIT, ("Setting default exclusion list %ls size=%d\r\n", buff, cbSize)); if (SetBandwidthConservationList(INVALID_HANDLE_VALUE, (USHORT *)buff, cbSize)) { fRet = TRUE; } #endif } if (lpwBandwidthConservationList) { LocalFree(lpwBandwidthConservationList); } return fRet; } BOOL ProcessNetArrivalMessage( VOID ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { vcntNetDevices = 1; ReportShareNetArrivalDeparture( 0, // all shares 0, FALSE, // don't invoke autodial TRUE // arrived ); ReintKdPrint(INIT, ("WM_DEVICECHANGE:Net arrived, %d nets so far\r\n", vcntNetDevices)); return (TRUE); } BOOL ProcessNetDepartureMessage( BOOL fInvokeAutodial ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { ReintKdPrint(MAINLOOP, ("WM_DEVICECHANGE:Net removed, %d nets so far\r\n", vcntNetDevices)); vcntNetDevices = 0; ReportShareNetArrivalDeparture( 0, // all shares 0, fInvokeAutodial, // invoke auto dial FALSE // departed ); return TRUE; } BOOL ReportShareNetArrivalDeparture( BOOL fOneServer, HSHARE hShare, BOOL fInvokeAutoDial, BOOL fArrival ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { SHAREINFO sSR; _TCHAR *lptzServerName; unsigned ulStatus; DWORD dwSize; BOOL fGotName = FALSE; BOOL fAllocated = FALSE; BOOL fRet = FALSE; LRESULT lResult = LRESULT_CSCFAIL; LPTSTR lp = NULL; DWORD dwMessage = 0; WPARAM dwWParam = (WPARAM) 0; LPARAM dwLParam = (LPARAM) 0; lptzServerName = sSR.rgSharePath; if (fOneServer) { if (fArrival) { if(GetShareInfo(vhShadowDBForEvent, hShare, &sSR, &ulStatus)<= 0) { PrintFn("ReportShareNetArrivalDeparture: couldn't get status for server 0x%x\r\n", hShare); ReintKdPrint( BADERRORS, ("ReportShareNetArrivalDeparture: couldn't get status for server 0x%x\r\n", hShare)); return FALSE; } lp = MyStrChr(sSR.rgSharePath+2, _T('\\')); if (!lp) { ReintKdPrint( BADERRORS, ("ReportShareNetArrivalDeparture: Invalid server name %ls\r\n", sSR.rgSharePath)); Assert(FALSE); return FALSE; } *lp = 0; } else { // A share departure int i; dwSize = sizeof(sSR.rgSharePath); fGotName = GetNameOfServerGoingOfflineEx( vhShadowDBForEvent, &lptzServerName, &dwSize, &fAllocated); if(!fGotName) { TransitionShareToOffline(vhShadowDBForEvent, fOneServer, 0xffffffff); goto bailout; } } } fRet = TRUE; ReintKdPrint( INIT, ("ReportShareNetArrivalDeparture: reporting %s to the systray\r\n", (fArrival) ? "arrival" : "departure")); dwMessage = (fArrival) ? STWM_CSCNETUP : STWM_CSCQUERYNETDOWN; dwWParam = (fInvokeAutoDial) ? ((hShare != 0) ? CSCUI_AUTODIAL_FOR_CACHED_SHARE : CSCUI_AUTODIAL_FOR_UNCACHED_SHARE) : CSCUI_NO_AUTODIAL; dwLParam = (fOneServer) ? (DWORD_PTR)(lptzServerName) : 0; lResult = ReportEventsToSystray(dwMessage, dwWParam, dwLParam); // if the redir is stuck waiting to be told whether to go offline on a share // tell him yes or no if (!fArrival) { if (fOneServer) { TransitionShareToOffline( vhShadowDBForEvent, fOneServer, (lResult == LRESULT_CSCWORKOFFLINE) ? 1 : ((lResult == LRESULT_CSCRETRY) ? 0 : 0xffffffff) ); if (lResult == LRESULT_CSCWORKOFFLINE) { dwMessage = STWM_CSCNETDOWN; dwWParam = fInvokeAutoDial; dwLParam = (fOneServer) ? ((hShare != 0) ? (DWORD_PTR)(lptzServerName) : 0xffffffff) : 0; ReportEventsToSystray(dwMessage, dwWParam, dwLParam); ReportTransitionToDfs(lptzServerName, TRUE, 0xffffffff); } } } bailout: if (fAllocated) { LocalFree(lptzServerName); } return (fRet); } BOOL CheckServerOnline( VOID ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { unsigned long ulStatus; WIN32_FIND_DATA sFind32; int cntReconnected=0; SHADOWINFO sSI; HANDLE hShadowDB; _TCHAR tzDriveMap[4]; CSC_ENUMCOOKIE ulEnumCookie=NULL; DWORD dwError; if (!ImpersonateALoggedOnUser()) { return 0; } if ((hShadowDB = OpenShadowDatabaseIO()) ==INVALID_HANDLE_VALUE) { ResetAgentThreadImpersonation(); return 0; } memset(&sFind32, 0, sizeof(sFind32)); lstrcpy(sFind32.cFileName, _TEXT("*")); if(FindOpenShadow( hShadowDB, 0, FINDOPEN_SHADOWINFO_ALL, &sFind32, &sSI)) { ulEnumCookie = sSI.uEnumCookie; do { if(GetShareStatus(hShadowDB, sSI.hShare, &ulStatus)) { if(ulStatus & SHARE_DISCONNECTED_OP){ dwError = DWConnectNet(sFind32.cFileName, tzDriveMap, NULL, NULL, NULL, 0, NULL); if ((dwError == NO_ERROR)||(dwError == ERROR_ACCESS_DENIED)||(dwError==WN_CONNECTED_OTHER_PASSWORD_DEFAULT)) { if (sSI.ulHintFlags & FLAG_CSC_HINT_PIN_SYSTEM) { TransitionShareToOnline(INVALID_HANDLE_VALUE, 0); } else { ReportShareNetArrivalDeparture( TRUE, sSI.hShare, // this share FALSE, // don't autodial TRUE); // Arrived } ++cntReconnected; if (dwError == NO_ERROR || dwError == WN_CONNECTED_OTHER_PASSWORD_DEFAULT) { DWDisconnectDriveMappedNet(tzDriveMap, TRUE); // force disconnect } } } } Sleep(200); } while(FindNextShadow(hShadowDB, ulEnumCookie, &sFind32, &sSI)); FindCloseShadow(hShadowDB, ulEnumCookie); } CloseShadowDatabaseIO(hShadowDB); ResetAgentThreadImpersonation(); return (cntReconnected?TRUE:FALSE); } BOOL AreAnyServersOffline( VOID) { ULONG ulStatus; WIN32_FIND_DATA sFind32 = {0}; SHADOWINFO sSI; HANDLE hShadowDB; CSC_ENUMCOOKIE ulEnumCookie = NULL; BOOL bFoundOne = FALSE; if ((hShadowDB = OpenShadowDatabaseIO()) ==INVALID_HANDLE_VALUE) return FALSE; wcscpy(sFind32.cFileName, L"*"); if (FindOpenShadow(hShadowDB, 0, FINDOPEN_SHADOWINFO_ALL, &sFind32, &sSI)) { ulEnumCookie = sSI.uEnumCookie; do { if (GetShareStatus(hShadowDB, sSI.hShare, &ulStatus)) { if ((ulStatus & SHARE_DISCONNECTED_OP) != 0) { bFoundOne = TRUE; break; } } } while(FindNextShadow(hShadowDB, ulEnumCookie, &sFind32, &sSI)); FindCloseShadow(hShadowDB, ulEnumCookie); } CloseShadowDatabaseIO(hShadowDB); return bFoundOne; } BOOL FAbortOperation( VOID ) { if (!vdwAgentThreadId) { return FALSE; } if (IsAgentShutDownRequested() || HasAgentShutDown()) { ReintKdPrint(MAINLOOP, ("CSC.FAbortOperation: Agentshutdown detected aborting \r\n")); return TRUE; } return (FStopAgent()); } VOID SetAgentShutDownRequest( VOID ) { fAgentShutDownRequested = TRUE; SetEvent(heventShutDownAgent); } BOOL IsAgentShutDownRequested( VOID ) { return (fAgentShutDownRequested == TRUE); } VOID SetAgentShutDown( VOID ) { fAgentShutDown = TRUE; } BOOL HasAgentShutDown( VOID ) { return (fAgentShutDown == TRUE ); } // HWND CSCUIInitialize(HANDLE hToken, DWORD dwFlags) BOOL InitCSCUI( HANDLE hToken ) { ReintKdPrint(INIT, (" Initializing cscui\r\n")); EnterAgentCrit(); if (!vhlibCSCUI) { vhlibCSCUI = LoadLibrary(vtzCSCUI); if (vhlibCSCUI) { if (vlpfnCSCUIInitialize = (CSCUIINITIALIZE)GetProcAddress(vhlibCSCUI, (const char *)vszCSCUIInitialize)) { if(!(vlpfnCSCUISetState = (CSCUISETSTATE)GetProcAddress(vhlibCSCUI, (const char *)vszCSCUISetState))) { ReintKdPrint(BADERRORS, ("Failed to get proc addres for %s, Error = %d \r\n", vszCSCUISetState, GetLastError())); } else { (*vlpfnCSCUIInitialize)(hToken, CI_INITIALIZE); } } else { ReintKdPrint(BADERRORS, ("Failed to get proc addres for %s, Error = %d \r\n", vszCSCUIInitialize, GetLastError())); } } else { ReintKdPrint(BADERRORS, ("Failed to load %ls, Error = %d \r\n", vtzCSCUI, GetLastError())); } } LeaveAgentCrit(); return (vlpfnCSCUISetState != NULL); } VOID TerminateCSCUI( VOID ) { BOOL fShowing; // snapshot the state of the showing dialog // if we are about to show an offline dialog, ReportEventsToSystray // will also do the action below but will try to set the vfShowingOfflineDlg // variable to 1 // If fShowing was not set to 1, then we know that we are not showing UI // and we set vfShowingOfflineDlg to 0xffffffff. This will make ReportEventsToSystray // to not show the offline popup, so we will be free to do FreeLibrary fShowing = (BOOL)InterlockedExchange((PLONG)&vfShowingOfflineDlg, 0xffffffff); if (fShowing==1) { Assert(vhlibCSCUI && vlpfnCSCUISetState); (vlpfnCSCUISetState)(STWM_CSCCLOSEDIALOGS, 0, 0); while (vfShowingOfflineDlg != 0xfffffffe) { Sleep(10); } } if (vhlibCSCUI) { (*vlpfnCSCUIInitialize)(0, CI_TERMINATE); vlpfnCSCUIInitialize = NULL; vlpfnCSCUISetState = NULL; FreeLibrary(vhlibCSCUI); vhlibCSCUI = NULL; } vfShowingOfflineDlg = 0; } LRESULT ReportEventsToSystray( DWORD dwMessage, WPARAM dwWParam, LPARAM dwLParam ) /*++ Routine Description: Parameters: Return Value: Notes: --*/ { LRESULT lResult = LRESULT_CSCFAIL; BOOL fOk; HWND hwnd; extern HDESK hdesktopUser, hdesktopCur; EnterAgentCrit(); // is there a currently logged on user? if (!hdesktopUser) { LeaveAgentCrit(); ReintKdPrint(INIT, ("ReportEventsToSystray: no-user logged, not-reporting\r\n")); return lResult; } else { if(!SetThreadDesktop(hdesktopUser)) { LeaveAgentCrit(); PrintFn("ReportEventsToSystray: failed to set desktop for agent thread error=%d\r\n", GetLastError()); ReintKdPrint(BADERRORS, ("ReportEventsToSystray: failed to set desktop for agent thread error=%d\r\n", GetLastError())); return lResult; } // set our desktop to be that of the loggedon users desktop hdesktopCur = hdesktopUser; } LeaveAgentCrit(); ReintKdPrint(INIT, ("ReportEventsToSystray: reporting message dwMessage=0x%x to the systray\r\n", dwMessage)); // snapshot the state of the showing dialog // if we are about to terminate, the the terminateCSCUI will have // will also do the action below but will try to set the vfShowingOfflineDlg // variable to 0xffffffff // If fOk was not set to 0xffffffff, then we know that we are not terminating // and we set vfShowingOfflineDlg to 1. This will block the terminating guy // and he will not free the library till thei variable gets set to FALSE; fOk = (BOOL)InterlockedExchange((PLONG)&vfShowingOfflineDlg, 1); if (fOk == 0) { if (vlpfnCSCUISetState) { lResult = (vlpfnCSCUISetState)(dwMessage, dwWParam, dwLParam); // change the value of vfShowingOfflineDlg to 0 only if it is 1 right now // it can be something other than 1 if we are about to terminate if((DWORD)InterlockedCompareExchange(&vfShowingOfflineDlg, 0, 1) == 0xffffffff) { // if we came here then we are terminating, set a termination value // so that the logoff thread will stop doing a sleep loop // and if we came here again fOk will never be 0 vfShowingOfflineDlg = 0xfffffffe; } } else { // Not showing any dialog, restore the variable back to what it should be vfShowingOfflineDlg = 0; PrintFn("ReportEventsToSystray: CSCUI not initalized\r\n"); } } return lResult; } BOOL GetNameOfServerGoingOfflineEx( HANDLE hShadowDB, _TCHAR **lplptzServerName, DWORD *lpdwSize, BOOL *lpfAllocated ) /*++ Routine Description: This routine is called in winlogon thread to findout which server is about to go offline The routine may allocate memory, which must be freed by the caller Parameters: Return Value: Notes: --*/ { DWORD dwSize, i; BOOL fRet = FALSE; dwSize = *lpdwSize; *lpfAllocated = FALSE; for (i=0;i<2;++i) { if(!GetNameOfServerGoingOffline( hShadowDB, (LPBYTE)(*lplptzServerName), &dwSize)) { // if we need a bigger buffer go get one if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // we shouldn't be trying to get a bigger buffer twice // it should have been sufficient the first time around if (i==1) { Assert(FALSE); break; } ReintKdPrint(MAINLOOP, ("GetNameOfServerGoingOfflineEx: Need %d sized Buffer \n", dwSize)); *lplptzServerName = LocalAlloc(LPTR, dwSize); if (!*lplptzServerName) { return FALSE; } *lpfAllocated = TRUE; *lpdwSize = dwSize; continue; } else { break; } } else { // ReintKdPrint(MAINLOOP,("GetNameOfServerGoingOfflineEx:name=%ws\n", *lplptzServerName)); fRet = TRUE; break; } } // cleanup on error if (!fRet && *lpfAllocated) { LocalFree(*lplptzServerName); *lpfAllocated = FALSE; } return fRet; } BOOL IsWinlogonRegValueSet(HKEY hKey, LPSTR pszKeyName, LPSTR pszPolicyKeyName, LPSTR pszValueName) { BOOL bRet = FALSE; DWORD dwType; DWORD dwSize; HKEY hkey; // // first check the per-machine location. // if (RegOpenKeyExA(hKey, pszKeyName, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(bRet); if (RegQueryValueExA(hkey, pszValueName, NULL, &dwType, (LPBYTE)&bRet, &dwSize) == ERROR_SUCCESS) { if (dwType != REG_DWORD) { bRet = FALSE; } } RegCloseKey(hkey); } // // then let the policy value override // if (RegOpenKeyExA(hKey, pszPolicyKeyName, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(bRet); if (RegQueryValueExA(hkey, pszValueName, NULL, &dwType, (LPBYTE)&bRet, &dwSize) == ERROR_SUCCESS) { if (dwType != REG_DWORD) { bRet = FALSE; } } RegCloseKey(hkey); } return bRet; } BOOL CheckIsSafeModeType(DWORD dwSafeModeType) { BOOL bResult = FALSE; HKEY hkey; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option"), 0, KEY_QUERY_VALUE, &hkey)) { DWORD dwType; DWORD dwValue; DWORD cbValue = sizeof(dwValue); if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("OptionValue"), NULL, &dwType, (LPBYTE)&dwValue, &cbValue)) { bResult = (dwValue == dwSafeModeType); } RegCloseKey(hkey); } return bResult; } BOOL AllowMultipleTsSessions(void) { return IsWinlogonRegValueSet(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system", "AllowMultipleTSSessions"); } BOOL IsSafeMode(void) { return CheckIsSafeModeType(SAFEBOOT_MINIMAL) || CheckIsSafeModeType(SAFEBOOT_NETWORK); } BOOL IsTerminalServicesEnabled(void) { OSVERSIONINFOEX osVersionInfo; DWORDLONG dwlConditionMask; dwlConditionMask = 0; ZeroMemory(&osVersionInfo, sizeof(osVersionInfo)); osVersionInfo.dwOSVersionInfoSize = sizeof(osVersionInfo); osVersionInfo.wSuiteMask = VER_SUITE_TERMINAL | VER_SUITE_SINGLEUSERTS; VER_SET_CONDITION(dwlConditionMask, VER_SUITENAME, VER_OR); return(VerifyVersionInfo(&osVersionInfo, VER_SUITENAME, dwlConditionMask) != FALSE); } BOOL IsMultipleUsersEnabled(void) { return IsTerminalServicesEnabled() && !IsSafeMode() && AllowMultipleTsSessions(); } BOOL CanCSCLivewithTS( VOID) /*++ Routine Description: Checks if TS settings are ok to turn on csc. Arguments: None Return Value: TRUE - Enable CSC FALSE - Disable CSC --*/ { if (IsWorkstation ()) // for pro and per { // csc does not work with Fast user switching. return !IsFastUserSwitchingEnabled(); } else // for servers { //Both conditions have to be true return !(IsTerminalServicesEnabled() && AreConnectionsAllowed()); } } BOOL AreConnectionsAllowed ( VOID) /*++ Routine Description: Checks if TS accepts connections. Arguments: None Return Value: TRUE - Accepts connections FALSE - Denys connections --*/ { DWORD dwError; HKEY hkey; dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\Terminal Server"), 0, KEY_QUERY_VALUE, &hkey); if (ERROR_SUCCESS == dwError) { DWORD dwType; DWORD dwValue; DWORD cbValue = sizeof(dwValue); if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("fDenyTSConnections"), NULL, &dwType, (LPBYTE)&dwValue, &cbValue)) { return !dwValue; } } // // could not read registry, this means connections were allowed. // return TRUE; } BOOL IsFastUserSwitchingEnabled( VOID) /*++ Routine Description: Checks to see if Terminal Services Fast User Switching is enabled. This is to check if we should use the physical console session for UI dialogs, or always use session 0. Fast User Switching exists only on workstation product version, where terminal services are available, when AllowMultipleTSSessions is set. On server and above, or when multiple TS users are not allowed, session 0 can only be attached remotely be special request, in which case it should be considered the "Console" session. Arguments: None. Return Value: Returns TRUE if Fast User Switching is currently enabled, FALSE otherwise. --*/ { static BOOL bVerified = FALSE; static BOOL bIsTSWorkstation = FALSE; HKEY hKey; ULONG ulSize, ulValue; BOOL bFusEnabled; // // Verify the product version if we haven't already. // if (!bVerified) { OSVERSIONINFOEX osvix; DWORDLONG dwlConditionMask = 0; ZeroMemory(&osvix, sizeof(OSVERSIONINFOEX)); osvix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); osvix.wProductType = VER_NT_WORKSTATION; VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_LESS_EQUAL); osvix.wSuiteMask = VER_SUITE_TERMINAL | VER_SUITE_SINGLEUSERTS; VER_SET_CONDITION(dwlConditionMask, VER_SUITENAME, VER_OR); if (VerifyVersionInfo(&osvix, VER_PRODUCT_TYPE | VER_SUITENAME, dwlConditionMask)) { bIsTSWorkstation = TRUE; } bVerified = TRUE; } // // Fast user switching (FUS) only applies to the Workstation product where // Terminal Services are enabled (i.e. Personal, Professional). // if (!bIsTSWorkstation) { return FALSE; } // // Check if multiple TS sessions are currently allowed. We can't make this // info static because it can change dynamically. // if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"), 0, KEY_READ, &hKey) != ERROR_SUCCESS) { return FALSE; } ulValue = 0; ulSize = sizeof(ulValue); bFusEnabled = FALSE; if (RegQueryValueEx(hKey, TEXT("AllowMultipleTSSessions"), NULL, NULL, (LPBYTE)&ulValue, &ulSize) == ERROR_SUCCESS) { bFusEnabled = (ulValue != 0); } RegCloseKey(hKey); return bFusEnabled; } // IsFastUserSwitchingEnabled BOOL IsWorkstation( VOID) /*++ Routine Description: Checks if the machine is a workstation. Arguments: None Return Value: TRUE - is workstation. FALSE - is not a workstation. --*/ { OSVERSIONINFOEX osvix; DWORDLONG dwlConditionMask = 0; ZeroMemory(&osvix, sizeof(OSVERSIONINFOEX)); osvix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); osvix.wProductType = VER_NT_WORKSTATION; VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_LESS_EQUAL); if (VerifyVersionInfo(&osvix, VER_PRODUCT_TYPE, dwlConditionMask)) { return TRUE; } return FALSE; }