/*++ Copyright (c) 1992 Microsoft Corporation Module Name: Shutdown.c Abstract: This module contains the server side implementation for the Win32 remote shutdown APIs, that is: - BaseInitiateSystemShutdown - BaseAbortSystemShutdown Author: Dave Chalmers (davidc) 29-Apr-1992 Notes: Revision History: 19-Oct-1993 Danl Removed HackExtraThread which was only here to work around a bug in the UserSrv. The text describing the reason for this workaround is as follows: HACKHACK - Work around bug in UserSrv that causes ExitWindowsEx to fail (error 5) when called by a process which doesn't have any threads which have called User APIs. Remove after UserSrv is fixed. See NTBUG 11601. Workaround is to create a thread which will make one API call then sleep forever. --*/ #include "precomp.h" #pragma hdrstop #define RPC_NO_WINDOWS_H #include "regrpc.h" #include "ntrpcp.h" #include <rpc.h> // // // // // // // // Shutdown Dialog Return Codes: // #define SHUTDOWN_SUCCESS 0 #define SHUTDOWN_USER_LOGOFF 1 #define SHUTDOWN_DESKTOP_SWITCH 2 #define SHUTDOWN_CANCELLED 3 // // System Shutdown globals // RTL_CRITICAL_SECTION ShutdownCriticalSection; // Protect global shutdown data // // Set when a thread has a shutdown 'in progress' // (Protected by critical section) // BOOL ShutdownInProgress; // // Set when a thread wants to interrupt the shutdown // (Protected by critical section) // BOOL AbortShutdown; // // Data for shutdown UI - this is protected by the ShutdownInProgress flag. // i.e. only the current shutdown thread manipulates this data. // LARGE_INTEGER ShutdownTime; DWORD ShutdownDelayInSeconds; PTCH ShutdownMessage; DWORD ExitWindowsFlags; DWORD GinaCode; PTSTR UserName; PTSTR UserDomain; BOOL AllowLogonDuringShutdown = TRUE ; ActiveDesktops ShutdownDesktop; WINDOWPLACEMENT ShutdownWindowPlacement; BOOL ShutdownGetPlacement = FALSE; BOOL ShutdownHasBegun = FALSE; // // Data captured during initialization // PGLOBALS GlobalWinMainData; // // Private prototypes // DWORD InitializeShutdownData( PUNICODE_STRING lpMessage, DWORD dwTimeout, BOOL bForceAppsClosed, BOOL bRebootAfterShutdown ); VOID FreeShutdownData( VOID ); BOOL WINAPI ShutdownApiDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ); BOOL UpdateTimeToShutdown( HWND hDlg ); VOID CentreWindow( HWND hwnd ); DWORD TestClientPrivilege( VOID ); DWORD GetClientId( PTSTR *UserName, PTSTR *UserDomain ); VOID DeleteClientId( PTSTR UserName, PTSTR UserDomain ); BOOL InsertClientId( HWND hDlg, int ControlId, PTSTR UserName, PTSTR UserDomain ); BOOL InitializeShutdownModule( PGLOBALS pGlobals ) /*++ Routine Description: Does any initializtion required for this module. Arguments: pGlobals - Pointer to global data defined in WinMain Return Value: Returns TRUE on success, FALSE on failure. --*/ { NTSTATUS Status; // // Initialize global variables // ShutdownInProgress = FALSE; // // Initialize critical section to protect globals // Status = RtlInitializeCriticalSection(&ShutdownCriticalSection); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("Registry Server : Shutdown : Failed to initialize critical section\n"); } #endif GlobalWinMainData = pGlobals; return(NT_SUCCESS(Status)); } ULONG BaseInitiateSystemShutdown( IN PREGISTRY_SERVER_NAME ServerName, IN PUNICODE_STRING lpMessage OPTIONAL, IN DWORD dwTimeout, IN BOOLEAN bForceAppsClosed, IN BOOLEAN bRebootAfterShutdown ) /*++ Routine Description: Initiates the shutdown of this machine. Arguments: ServerName - Name of machine this server code is running on. (Ignored) lpMessage - message to display during shutdown timeout period. dwTimeout - number of seconds to delay before shutting down bForceAppsClosed - Normally applications may prevent system shutdown. - If this true, all applications are terminated unconditionally. bRebootAfterShutdown - TRUE if the system should reboot. FALSE if it should - be left in a shutdown state. Return Value: Returns ERROR_SUCCESS (0) for success; error-code for failure. --*/ { NTSTATUS Status; DWORD Error; // // Check the caller has the appropriate privilege // Error = TestClientPrivilege(); if (Error != ERROR_SUCCESS) { return(Error); } // // Enter the critical section so we can look at our globals // Status = RtlEnterCriticalSection(&ShutdownCriticalSection); if (!NT_SUCCESS(Status)) { return(RtlNtStatusToDosError(Status)); } // // Set up our global shutdown data. // Fail if a shutdown is already in progress // if (ShutdownInProgress) { Error = ERROR_SHUTDOWN_IN_PROGRESS; } else { // // Set up our globals for the shutdown thread to use. // Error = InitializeShutdownData(lpMessage, dwTimeout, bForceAppsClosed, bRebootAfterShutdown ); if (Error == ERROR_SUCCESS) { ShutdownInProgress = TRUE; AbortShutdown = FALSE; } } // // Leave the critical section // Status = RtlLeaveCriticalSection(&ShutdownCriticalSection); if (Error == ERROR_SUCCESS) { if (!NT_SUCCESS(Status)) { Error = RtlNtStatusToDosError(Status); } } else { ASSERT(NT_SUCCESS(Status)); } // // Create a thread to handle the shutdown (UI and calling ExitWindows) // The thread will handle resetting our shutdown data and globals. // if (Error == ERROR_SUCCESS) { int Result; // // Have winlogon create us a thread running on the user's desktop. // // The thread will do a call back to ShutdownThread() // GlobalWinMainData->LogoffFlags = EWX_WINLOGON_API_SHUTDOWN | ExitWindowsFlags; Result = InitiateLogoff( GlobalWinMainData, EWX_WINLOGON_API_SHUTDOWN | ExitWindowsFlags ); if (Result != DLG_SUCCESS ) { Error = GetLastError(); KdPrint(("InitiateSystemShutdown : Failed to create shutdown thread. Error = %d\n", Error)); FreeShutdownData(); ShutdownInProgress = FALSE; // Atomic operation } } return(Error); UNREFERENCED_PARAMETER(ServerName); } DWORD InitializeShutdownData( PUNICODE_STRING lpMessage, DWORD dwTimeout, BOOL bForceAppsClosed, BOOL bRebootAfterShutdown ) /*++ Routine Description: Stores the passed shutdown parameters in our global data. Return Value: Returns ERROR_SUCCESS (0) for success; error-code for failure. --*/ { NTSTATUS Status; LARGE_INTEGER TimeNow; LARGE_INTEGER Delay; DWORD Error; // // Set the shutdown time // ShutdownDelayInSeconds = dwTimeout; Status = NtQuerySystemTime(&TimeNow); if (!NT_SUCCESS(Status)) { return(RtlNtStatusToDosError(Status)); } Delay = RtlEnlargedUnsignedMultiply(dwTimeout, 10000000); // Delay in 100ns ShutdownTime.QuadPart = TimeNow.QuadPart + Delay.QuadPart; // // Set the shutdown flags // // We set the EWX_WINLOGON_OLD_xxx and EWX_xxx both since this message // originates from the winlogon process. When these flags actually bubble // back to the active dialog box, winlogon expects the EWX_WINLOGON_OLD_xxx // to indicate the 'real' request. // ExitWindowsFlags = EWX_LOGOFF | EWX_SHUTDOWN | EWX_WINLOGON_OLD_SHUTDOWN; ExitWindowsFlags |= bForceAppsClosed ? EWX_FORCE : 0; ExitWindowsFlags |= bRebootAfterShutdown ? (EWX_REBOOT | EWX_WINLOGON_OLD_REBOOT) : 0; if (bRebootAfterShutdown) { GinaCode = WLX_SAS_ACTION_SHUTDOWN_REBOOT; } else { GinaCode = WLX_SAS_ACTION_SHUTDOWN; } // // Store the caller's username and domain. // Error = GetClientId(&UserName, &UserDomain); if (Error != ERROR_SUCCESS) { return(Error); } // // Set the shutdown message // if (lpMessage != NULL) { // // Copy the message into a global buffer // USHORT Bytes = lpMessage->Length + (USHORT)sizeof(UNICODE_NULL); ShutdownMessage = (PTCH)LocalAlloc(LPTR, Bytes); if (ShutdownMessage == NULL) { DeleteClientId(UserName, UserDomain); return(ERROR_NOT_ENOUGH_MEMORY); } RtlMoveMemory(ShutdownMessage, lpMessage->Buffer, lpMessage->Length); ShutdownMessage[lpMessage->Length / sizeof(WCHAR)] = 0; // Null terminate } else { ShutdownMessage = NULL; } return(ERROR_SUCCESS); } VOID FreeShutdownData( VOID ) /*++ Routine Description: Frees up any memory allocated to store the shutdown data Return Value: None. --*/ { if (ShutdownMessage != NULL) { LocalFree(ShutdownMessage); ShutdownMessage = NULL; } DeleteClientId(UserName, UserDomain); UserName = NULL; UserDomain = NULL; } BOOLEAN ShutdownThread( VOID ) /*++ Routine Description: Handles the display of a shutdown dialog and coordinating with the AbortShutdown API. Arguments: None Return Value: TRUE - system should be shut down FALSE - shutdown was aborted --*/ { NTSTATUS Status; DWORD Error; BOOL DoShutdown = TRUE; HDESK hdesk; BOOL CloseDesktopHandle; DWORD Result; BOOL Locked; BOOL Success; // // Quick check so we don't get into thorny race conditions. // if ( ShutdownDelayInSeconds == 0 ) { FreeShutdownData(); RtlEnterCriticalSection( &ShutdownCriticalSection ); ShutdownInProgress = FALSE ; RtlLeaveCriticalSection( &ShutdownCriticalSection ); GlobalWinMainData->LastGinaRet = GinaCode; ShutdownHasBegun = TRUE; return( TRUE ); } hdesk = GetActiveDesktop(&GlobalWinMainData->WindowStation, &CloseDesktopHandle, &Locked); while (hdesk != NULL) { DebugLog((DEB_TRACE, "Starting shutdown dialog on desktop %x\n", hdesk)); if (Locked) { UnlockWindowStation(GlobalWinMainData->WindowStation.hwinsta); } Success = SetThreadDesktop(hdesk); if (!Success) { DebugLog((DEB_TRACE, "Unable to set desktop, %d\n", GetLastError())); } if (Locked) { LockWindowStation(GlobalWinMainData->WindowStation.hwinsta); } ShutdownDesktop = GlobalWinMainData->WindowStation.ActiveDesktop; // // Push the timeout past the shutdown delay, so that we can // catch the messages we want, without stomping on the timeout // structures. // Result = DialogBoxParam( GetModuleHandle(NULL), MAKEINTRESOURCE( IDD_SYSTEM_SHUTDOWN ), NULL, ShutdownApiDlgProc, (LPARAM) 0 ); DebugLog((DEB_TRACE, "Shutdown Dialog Returned %d\n", Result )); if (CloseDesktopHandle) { CloseDesktop( hdesk ); } if ((Result == SHUTDOWN_SUCCESS) || (Result == SHUTDOWN_CANCELLED) ) { break; } // // Trickier ones: // if (Result == SHUTDOWN_USER_LOGOFF) { if (!AllowLogonDuringShutdown) { break; } } ShutdownGetPlacement = TRUE; hdesk = GetActiveDesktop(&GlobalWinMainData->WindowStation, &CloseDesktopHandle, &Locked); DebugLog((DEB_TRACE, "Switching to current desktop and restarting dialog\n")); } // // The shutdown has either completed or been cancelled // Reset our globals. // // Note we need to reset the shutdown-in-progress flag before // entering the non-abortable part of shutdown so that anyone // trying to abort from here on in will get a failure return code. // FreeShutdownData(); Status = RtlEnterCriticalSection(&ShutdownCriticalSection); Error = RtlNtStatusToDosError(Status); if (Error == ERROR_SUCCESS) { // // Reset the global shutdown-in-progress flag // and check for an abort request. // if (AbortShutdown) { DoShutdown = FALSE; } ShutdownInProgress = FALSE; // // Leave the critical section // Status = RtlLeaveCriticalSection(&ShutdownCriticalSection); if (!NT_SUCCESS(Status)) { Error = RtlNtStatusToDosError(Status); } } // // If DoShutdown, update the last gina ret so that // the shutdown code will know what to do: // if ( DoShutdown ) { GlobalWinMainData->LastGinaRet = GinaCode; ShutdownHasBegun = TRUE; } // // Tell the caller if he should shut down. // return DoShutdown; } BOOL WINAPI ShutdownApiDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) /*++ Routine Description: Processes messages for the shutdown dialog Dialog returns ERROR_SUCCESS if shutdown should proceed, ERROR_OPERATION_ABORTED if shutdown should be cancelled. --*/ { HMENU hMenu; switch (message) { case WM_INITDIALOG: // // Add the caller's id to the main message text // InsertClientId(hDlg, IDD_SYSTEM_MESSAGE, UserName, UserDomain); // // Setup the client's message // SetDlgItemText(hDlg, IDD_MESSAGE, ShutdownMessage); // // Remove the close item from the system menu // hMenu = GetSystemMenu(hDlg, FALSE); DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); // // Position ourselves // if ( ShutdownGetPlacement ) { SetWindowPlacement( hDlg, &ShutdownWindowPlacement ); } else { CentreWindow(hDlg); } SetWindowPos( hDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW ); // // Start the timer // SetTimer(hDlg, 0, 1000, NULL); // 1 second timer // // Check if it's over before we've even started // if (UpdateTimeToShutdown(hDlg)) { // It's already time to shutdown EndDialog(hDlg, SHUTDOWN_SUCCESS); } // // Let everyone know what state we're in // GlobalWinMainData->PreviousWinlogonState = GlobalWinMainData->WinlogonState; GlobalWinMainData->WinlogonState = Winsta_InShutdownDlg; // GlobalWinMainData->ShutdownStarted = TRUE; return(TRUE); case WM_TIMER: // // Check for abort flag // if (AbortShutdown) { if (GlobalWinMainData->WinlogonState == Winsta_InShutdownDlg) { GlobalWinMainData->WinlogonState = GlobalWinMainData->PreviousWinlogonState; } // GlobalWinMainData->ShutdownStarted = FALSE; EndDialog(hDlg, SHUTDOWN_CANCELLED); return(TRUE); } if ( GlobalWinMainData->WindowStation.ActiveDesktop != ShutdownDesktop ) { GetWindowPlacement( hDlg, &ShutdownWindowPlacement ); EndDialog( hDlg, SHUTDOWN_DESKTOP_SWITCH ); return( TRUE ); } // // Update the time delay and check if our time's up // if (!UpdateTimeToShutdown(hDlg)) { // // Keep waiting // return(TRUE); } // // Shutdown time has arrived. Drop through... // case WLX_WM_SAS: DebugLog((DEB_TRACE, "Sas message received? wParam = %d\n", wParam )); if ((wParam == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) && (message == WLX_WM_SAS)) { // // Don't end the dialog if it's just a screen saver timeout // return(TRUE); } else if ((wParam == WLX_SAS_TYPE_CTRL_ALT_DEL) && (message == WLX_WM_SAS)) { // // Also don't end the dialog if it's a Ctrl-Alt-Del // Sleep (1000); return(TRUE); } else { // // If the user logs off, preempt the timeout, restore the state // if (GlobalWinMainData->WinlogonState == Winsta_InShutdownDlg) { GlobalWinMainData->WinlogonState = GlobalWinMainData->PreviousWinlogonState; } EndDialog(hDlg, SHUTDOWN_SUCCESS); return(TRUE); } } // We didn't process this message return FALSE; UNREFERENCED_PARAMETER(lParam); } BOOL UpdateTimeToShutdown( HWND hDlg ) /*++ Routine Description: Updates the display of the time to system shutdown. Returns: TRUE if shutdown time has arrived, otherwise FALSE --*/ { NTSTATUS Status; BOOLEAN Success; LARGE_INTEGER TimeNow; ULONG ElapsedSecondsNow; ULONG ElapsedSecondsAtShutdown; ULONG SecondsRemaining; ULONG DaysRemaining; ULONG HoursRemaining; ULONG MinutesRemaining; TCHAR Message[40]; // // Set the shutdown time // Status = NtQuerySystemTime(&TimeNow); ASSERT(NT_SUCCESS(Status)); if (TimeNow.QuadPart >= ShutdownTime.QuadPart) { return(TRUE); } Success = RtlTimeToSecondsSince1980(&TimeNow, &ElapsedSecondsNow); ASSERT(Success); Success = RtlTimeToSecondsSince1980(&ShutdownTime, &ElapsedSecondsAtShutdown); ASSERT(Success); SecondsRemaining = ElapsedSecondsAtShutdown - ElapsedSecondsNow; // // Convert the seconds remaining to a string // MinutesRemaining = SecondsRemaining / 60; HoursRemaining = MinutesRemaining / 60; DaysRemaining = HoursRemaining / 24; SecondsRemaining = SecondsRemaining % 60; MinutesRemaining = MinutesRemaining % 60; HoursRemaining = HoursRemaining % 24; if (DaysRemaining > 0) { wsprintf(Message, TEXT("%d days"), DaysRemaining); } else { wsprintf(Message, TEXT("%02d:%02d:%02d"), HoursRemaining, MinutesRemaining, SecondsRemaining); } SetDlgItemText(hDlg, IDD_TIMER, Message); return(FALSE); } ULONG BaseAbortSystemShutdown( IN PREGISTRY_SERVER_NAME ServerName ) /*++ Routine Description: Aborts a pending shutdown of this machine. Arguments: ServerName - Name of machine this server code is running on. (Ignored) Return Value: Returns ERROR_SUCCESS (0) for success; error-code for failure. --*/ { NTSTATUS Status; DWORD Error; // // Check the caller has the appropriate privilege // Error = TestClientPrivilege(); if (Error != ERROR_SUCCESS) { return(Error); } // // Enter the critical section so we can look at our globals // Status = RtlEnterCriticalSection(&ShutdownCriticalSection); if (!NT_SUCCESS(Status)) { return(RtlNtStatusToDosError(Status)); } // // If a shutdown is in progress, set the abort flag // if (ShutdownInProgress) { AbortShutdown = TRUE; Error = ERROR_SUCCESS; } else { if ( ShutdownHasBegun ) { Error = ERROR_SHUTDOWN_IN_PROGRESS; } else { Error = ERROR_NO_SHUTDOWN_IN_PROGRESS; } } // // Leave the critical section // Status = RtlLeaveCriticalSection(&ShutdownCriticalSection); if (Error == ERROR_SUCCESS) { if (!NT_SUCCESS(Status)) { Error = RtlNtStatusToDosError(Status); } } else { ASSERT(NT_SUCCESS(Status)); } return(Error); UNREFERENCED_PARAMETER(ServerName); } DWORD TestClientPrivilege( VOID ) /*++ Routine Description: Checks if the client has the privilege to perform the requested shutdown. Arguments: None Return Value: ERROR_SUCCESS if the client has the appropriate privilege. ERROR_ACCESS_DENIED - client does not have the required privilege --*/ { NTSTATUS Status, IgnoreStatus; BOOL LocalConnection; LUID PrivilegeRequired; PRIVILEGE_SET PrivilegeSet; BOOLEAN Privileged; USER_SESSION_KEY SessionKey; HANDLE Token; UNICODE_STRING SubSystemName; // LATER this should be global RtlInitUnicodeString(&SubSystemName, L"Win32 Registry/SystemShutdown module"); // // Find out if this is a local connection // Status = RtlGetUserSessionKeyServer(NULL, &SessionKey); if (NT_SUCCESS(Status)) { LocalConnection = (Status == STATUS_LOCAL_USER_SESSION_KEY); if (LocalConnection) { PrivilegeRequired = RtlConvertLongToLuid(SE_SHUTDOWN_PRIVILEGE); } else { PrivilegeRequired = RtlConvertLongToLuid(SE_REMOTE_SHUTDOWN_PRIVILEGE); } // // See if the client has the required privilege // Status = I_RpcMapWin32Status(RpcImpersonateClient( NULL )); if (NT_SUCCESS(Status)) { PrivilegeSet.PrivilegeCount = 1; PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY; PrivilegeSet.Privilege[0].Luid = PrivilegeRequired; PrivilegeSet.Privilege[0].Attributes = 0; Status = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, &Token); if (NT_SUCCESS(Status)) { Status = NtPrivilegeCheck(Token, &PrivilegeSet, &Privileged); if (NT_SUCCESS(Status) || (Status == STATUS_PRIVILEGE_NOT_HELD)) { Status = NtPrivilegeObjectAuditAlarm( &SubSystemName, NULL, Token, 0, &PrivilegeSet, Privileged); } IgnoreStatus = NtClose(Token); ASSERT(NT_SUCCESS(IgnoreStatus)); } } IgnoreStatus = I_RpcMapWin32Status(RpcRevertToSelf()); ASSERT( NT_SUCCESS(IgnoreStatus) ); } // // Handle unexpected errors // if (!NT_SUCCESS(Status)) { return(RtlNtStatusToDosError(Status)); } // // If they failed the privilege check, return an error // if (!Privileged) { return(ERROR_ACCESS_DENIED); } // // They passed muster // return(ERROR_SUCCESS); } DWORD GetClientId( PTSTR *UserName, PTSTR *UserDomain ) /*++ Routine Description: Gets the name and domain of the caller, allocates and returns pointers to the information. Note we have RPC impersonate the client to discover their ID. Arguments: UserName - a pointer to a NULL terminated string containing the client's user name is returned here. DomainName - a pointer to a NULL terminated string containing the client's domain name is returned here. The caller should free UserName and DomainName by calling DeleteClientId Return Value: ERROR_SUCCESS - UserName and UserDomain contain valid pointers Other - UserName and UserDomain are invalid --*/ { HANDLE TokenHandle; DWORD cbNeeded; PTOKEN_USER pUserToken; BOOL ReturnValue=FALSE; DWORD cbDomain; DWORD cbName; SID_NAME_USE SidNameUse; DWORD Error; DWORD IgnoreError; // // Prepare for failure // *UserName = NULL; *UserDomain = NULL; Error = RpcImpersonateClient(NULL); if (Error != ERROR_SUCCESS) { return(Error); } if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &TokenHandle)) { // // Get the user Sid // if (!GetTokenInformation(TokenHandle, TokenUser, (PVOID)NULL, 0, &cbNeeded)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { pUserToken = (PTOKEN_USER)LocalAlloc(LPTR, cbNeeded); if (pUserToken != NULL) { if (GetTokenInformation(TokenHandle, TokenUser, pUserToken, cbNeeded, &cbNeeded)) { // // Convert User Sid to name/domain // cbName = 0; cbDomain = 0; if (!LookupAccountSid(NULL, pUserToken->User.Sid, NULL, &cbName, NULL, &cbDomain, &SidNameUse)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { *UserDomain = (PTSTR)LocalAlloc(LPTR, cbDomain*sizeof(TCHAR)); *UserName = (PTSTR)LocalAlloc(LPTR, cbName*sizeof(TCHAR)); if ((*UserDomain != NULL) && (*UserName != NULL)) { ReturnValue = LookupAccountSid( NULL, pUserToken->User.Sid, *UserName, &cbName, *UserDomain, &cbDomain, &SidNameUse); } } } } LocalFree(pUserToken); } } } CloseHandle(TokenHandle); } IgnoreError = RpcRevertToSelf(); ASSERT(IgnoreError == ERROR_SUCCESS); // // Clean up on failure // if (ReturnValue) { Error = ERROR_SUCCESS; } else { Error = GetLastError(); DeleteClientId(*UserName, *UserDomain); *UserName = NULL; *UserDomain = NULL; } return(Error); } VOID DeleteClientId( PTSTR UserName, PTSTR UserDomain ) /*++ Routine Description: Frees the client id returned previously by GetClientId Arguments: UserName - a pointer to the username returned by GetClientId. DomainName - a pointer to the domain name eturned by GetClientId Return Value: None --*/ { if (UserName != NULL) { LocalFree(UserName); } if (UserDomain != NULL) { LocalFree(UserDomain); } } BOOL InsertClientId( HWND hDlg, int ControlId, PTSTR UserName, PTSTR UserDomain ) /*++ Routine Description: Takes the text from the specified dialog control, treats it as a printf formatting string and inserts the user name and domain as the first 2 string identifiers (%s) Arguments: UserName - a pointer to the username returned by GetClientId. DomainName - a pointer to the domain name eturned by GetClientId Return Value: TRUE on success, FALSE on failure --*/ { DWORD StringLength; DWORD StringBytes; PTSTR FormatBuffer; PTSTR Buffer; // // Allocate space for the formatting string out of the control // StringLength = (DWORD)SendMessage(GetDlgItem(hDlg, ControlId), WM_GETTEXTLENGTH, 0, 0); StringBytes = (StringLength + 1) * sizeof(TCHAR); // Allow for terminator FormatBuffer = (PTSTR)LocalAlloc(LPTR, StringBytes); if (FormatBuffer == NULL) { return(FALSE); } // // Read the format string into the buffer // GetDlgItemText(hDlg, ControlId, FormatBuffer, StringLength); // // Calculate the maximum size of the string we'll create // i.e. Formatting string + username + userdomain // StringLength += lstrlen(UserName); StringLength += lstrlen(UserDomain); // // Allocate space for formatted string // StringBytes = (StringLength + 1) * sizeof(TCHAR); // Allow for terminator Buffer = (PTSTR)LocalAlloc(LPTR, StringBytes); if (Buffer == NULL) { LocalFree(FormatBuffer); return(FALSE); } // // Insert the user id into the format string // wsprintf(Buffer, FormatBuffer, UserDomain, UserName); ASSERT((lstrlen(Buffer) * sizeof(TCHAR)) < StringBytes); // // Replace the control text with our formatted result // SetDlgItemText(hDlg, ControlId, Buffer); // // Tidy up // LocalFree(FormatBuffer); LocalFree(Buffer); return(TRUE); }