/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: miscutil.c Abstract: Miscellaneous utility functions for SPUTILS Author: Ted Miller (tedm) 20-Jan-1995 Revision History: Jamie Hunter (JamieHun) Jun-27-2000 Moved various functions out of setupapi into sputils Jamie Hunter (JamieHun) Mar-05-2002 Security code review --*/ #include "precomp.h" #pragma hdrstop PTSTR pSetupDuplicateString( IN PCTSTR String ) /*++ Routine Description: Create a duplicate copy of a nul-terminated string. If the string pointer is not valid an exception is generated. Arguments: String - supplies string to be duplicated. Return Value: NULL if out of memory. Caller can free buffer with pSetupFree(). --*/ { PTSTR p; // // The win32 lstrlen and lstrcpy functions are guarded with // try/except (at least on NT). So if we use them and the string // is invalid, we may end up 'laundering' it into a valid 0-length // string. We don't want that -- we actually want to fault // in that case. So use the CRT functions, which we know are // unguarded and will generate exceptions with invalid args. // // Also handle the case where the string is valid when we are // taking its length, but becomes invalid before or while we // are copying it. If we're not careful this could be a memory // leak. A try/finally does exactly what we want -- allowing us // to clean up and still 'pass on' the exception. // if(p = pSetupCheckedMalloc((_tcslen(String)+1)*sizeof(TCHAR))) { try { // // If String is or becomes invalid, this will generate // an exception, but before execution leaves this routine // we'll hit the termination handler. // _tcscpy(p,String); } finally { // // If a fault occurred during the copy, free the copy. // Execution will then pass to whatever exception handler // might exist in the caller, etc. // if(AbnormalTermination()) { pSetupFree(p); p = NULL; } } } return(p); } #ifndef SPUTILSW PSTR pSetupUnicodeToMultiByte( IN PCWSTR UnicodeString, IN UINT Codepage ) /*++ Routine Description: Convert a string from unicode to ansi. Arguments: UnicodeString - supplies string to be converted. Codepage - supplies codepage to be used for the conversion. Return Value: NULL if out of memory or invalid codepage. Caller can free buffer with pSetupFree(). --*/ { UINT WideCharCount; PSTR String; UINT StringBufferSize; UINT BytesInString; PSTR p; WideCharCount = lstrlenW(UnicodeString) + 1; // // Allocate maximally sized buffer. // If every unicode character is a double-byte // character, then the buffer needs to be the same size // as the unicode string. Otherwise it might be smaller, // as some unicode characters will translate to // single-byte characters. // StringBufferSize = WideCharCount * 2; String = pSetupCheckedMalloc(StringBufferSize); if(String == NULL) { return(NULL); } // // Perform the conversion. // BytesInString = WideCharToMultiByte( Codepage, 0, // default composite char behavior UnicodeString, WideCharCount, String, StringBufferSize, NULL, NULL ); if(BytesInString == 0) { pSetupFree(String); return(NULL); } // // Resize the string's buffer to its correct size. // If the realloc fails for some reason the original // buffer is not freed. // if(p = pSetupRealloc(String,BytesInString)) { String = p; } return(String); } PWSTR pSetupMultiByteToUnicode( IN PCSTR String, IN UINT Codepage ) /*++ Routine Description: Convert a string to unicode. Arguments: String - supplies string to be converted. Codepage - supplies codepage to be used for the conversion. Return Value: NULL if string could not be converted (out of memory or invalid cp) Caller can free buffer with pSetupFree(). --*/ { UINT BytesIn8BitString; UINT CharsInUnicodeString; PWSTR UnicodeString; PWSTR p; BytesIn8BitString = lstrlenA(String) + 1; // // Allocate maximally sized buffer. // If every character is a single-byte character, // then the buffer needs to be twice the size // as the 8bit string. Otherwise it might be smaller, // as some characters are 2 bytes in their unicode and // 8bit representations. // UnicodeString = pSetupCheckedMalloc(BytesIn8BitString * sizeof(WCHAR)); if(UnicodeString == NULL) { return(NULL); } // // Perform the conversion. // CharsInUnicodeString = MultiByteToWideChar( Codepage, MB_PRECOMPOSED, String, BytesIn8BitString, UnicodeString, BytesIn8BitString ); if(CharsInUnicodeString == 0) { pSetupFree(UnicodeString); return(NULL); } // // Resize the unicode string's buffer to its correct size. // If the realloc fails for some reason the original // buffer is not freed. // if(p = pSetupRealloc(UnicodeString,CharsInUnicodeString*sizeof(WCHAR))) { UnicodeString = p; } return(UnicodeString); } #endif // ! SPUTILSW #ifdef UNICODE DWORD pSetupCaptureAndConvertAnsiArg( IN PCSTR AnsiString, OUT PCWSTR *UnicodeString ) /*++ Routine Description: Capture an ANSI string whose validity is suspect and convert it into a Unicode string. The conversion is completely guarded and thus won't fault, leak memory in the error case, etc. Arguments: AnsiString - supplies string to be converted. UnicodeString - if successful, receives pointer to unicode equivalent of AnsiString. Caller must free with pSetupFree(). If not successful, receives NULL. This parameter is NOT validated so be careful. Return Value: Win32 error code indicating outcome. NO_ERROR - success, UnicodeString filled in. ERROR_NOT_ENOUGH_MEMORY - insufficient memory for conversion. ERROR_INVALID_PARAMETER - AnsiString was invalid. --*/ { PSTR ansiString; DWORD d; // // Capture the string first. We do this because pSetupMultiByteToUnicode // won't fault if AnsiString were to become invalid, meaning we could // 'launder' a bogus argument into a valid one. Be careful not to // leak memory in the error case, etc (see comments in DuplicateString()). // Do NOT use Win32 string functions here; we rely on faults occuring // when pointers are invalid! // *UnicodeString = NULL; d = NO_ERROR; try { ansiString = pSetupCheckedMalloc(strlen(AnsiString)+1); if(!ansiString) { d = ERROR_NOT_ENOUGH_MEMORY; } } except(EXCEPTION_EXECUTE_HANDLER) { // // If we get here, strlen faulted and ansiString // was not allocated. // d = ERROR_INVALID_PARAMETER; } if(d == NO_ERROR) { try { strcpy(ansiString,AnsiString); } except(EXCEPTION_EXECUTE_HANDLER) { d = ERROR_INVALID_PARAMETER; pSetupFree(ansiString); } } if(d == NO_ERROR) { // // Now we have a local copy of the string; don't worry // about faults any more. // *UnicodeString = pSetupMultiByteToUnicode(ansiString,CP_ACP); if(*UnicodeString == NULL) { d = ERROR_NOT_ENOUGH_MEMORY; } pSetupFree(ansiString); } return(d); } #else DWORD pSetupCaptureAndConvertAnsiArg( IN PCSTR AnsiString, OUT PCWSTR *UnicodeString ) { // // Stub so the dll will link. // UNREFERENCED_PARAMETER(AnsiString); UNREFERENCED_PARAMETER(UnicodeString); return(ERROR_CALL_NOT_IMPLEMENTED); } #endif BOOL pSetupConcatenatePaths( IN OUT PTSTR Target, IN PCTSTR Path, IN UINT TargetBufferSize, OUT PUINT RequiredSize OPTIONAL ) /*++ Routine Description: Concatenate 2 paths, ensuring that one, and only one, path separator character is introduced at the junction point. Arguments: Target - supplies first part of path. Path is appended to this. (Target should be a buffer under program control) Path - supplies path to be concatenated to Target. (Path should be a buffer under program control) TargetBufferSize - supplies the size of the Target buffer, in characters. RequiredSize - if specified, receives the number of characters required to hold the fully concatenated path, including the terminating nul. Return Value: TRUE if the full path fit in Target buffer. Otherwise the path will have been truncated. --*/ { UINT TargetLength,PathLength; BOOL TrailingBackslash,LeadingBackslash; UINT EndingLength; PCTSTR CharTest; TargetLength = lstrlen(Target); PathLength = lstrlen(Path); // // See whether the target has a trailing backslash. // (allow forward slash here which is symantically equivalent) // if(TargetLength && ((*(CharTest = CharPrev(Target,Target+TargetLength)) == TEXT('\\')) || (*CharTest == TEXT('/')))) { TrailingBackslash = TRUE; TargetLength--; } else { TrailingBackslash = FALSE; } // // See whether the path has a leading backshash. // (allow forward slash here which is symantically equivalent) // if((Path[0] == TEXT('\\')) || (Path[0] == TEXT('/'))) { LeadingBackslash = TRUE; PathLength--; } else { LeadingBackslash = FALSE; } // // Calculate the ending length, which is equal to the sum of // the length of the two strings modulo leading/trailing // backslashes, plus one path separator, plus a nul. // EndingLength = TargetLength + PathLength + 2; if(RequiredSize) { *RequiredSize = EndingLength; } if(!LeadingBackslash && (TargetLength < TargetBufferSize)) { Target[TargetLength++] = TEXT('\\'); } if(TargetBufferSize > TargetLength) { lstrcpyn(Target+TargetLength,Path,TargetBufferSize-TargetLength); } // // Make sure the buffer is nul terminated in all cases. // if (TargetBufferSize) { Target[TargetBufferSize-1] = 0; } return(EndingLength <= TargetBufferSize); } PCTSTR pSetupGetFileTitle( IN PCTSTR FilePath ) /*++ Routine Description: This routine returns a pointer to the first character in the filename part of the supplied path. If only a filename was given, then this will be a pointer to the first character in the string (i.e., the same as what was passed in). To find the filename part, the routine returns the last component of the string, beginning with the character immediately following the last '\', '/' (Windows treats '/' as equivalent to '\' ) or initial 'd:' specification. Arguments: FilePath - Supplies the file path from which to retrieve the filename portion. Return Value: A pointer to the beginning of the filename portion of the path. --*/ { PCTSTR LastComponent; TCHAR CurChar; if((_totupper(FilePath[0])>=TEXT('A')) && (_totupper(FilePath[0])<=TEXT('Z')) && (FilePath[1] == TEXT(':'))) { // // x: (drive letter - colon) is skipped, this // is the only time we don't treat ':' as part of path name // FilePath+=2; } LastComponent = FilePath; while(CurChar = *FilePath) { FilePath = CharNext(FilePath); if((CurChar == TEXT('\\')) || (CurChar == TEXT('/'))) { LastComponent = FilePath; } } return LastComponent; } #ifndef SPUTILSW BOOL _pSpUtilsInitializeSynchronizedAccess( OUT PMYLOCK Lock ) /*++ Routine Description: Initialize a lock structure to be used with Synchronization routines. Arguments: Lock - supplies structure to be initialized. This routine creates the locking event and mutex and places handles in this structure. Return Value: TRUE if the lock structure was successfully initialized. FALSE if not. --*/ { if(Lock->Handles[TABLE_DESTROYED_EVENT] = CreateEvent(NULL,TRUE,FALSE,NULL)) { if(Lock->Handles[TABLE_ACCESS_MUTEX] = CreateMutex(NULL,FALSE,NULL)) { return(TRUE); } CloseHandle(Lock->Handles[TABLE_DESTROYED_EVENT]); } return(FALSE); } VOID _pSpUtilsDestroySynchronizedAccess( IN OUT PMYLOCK Lock ) /*++ Routine Description: Tears down a lock structure created by InitializeSynchronizedAccess. ASSUMES THAT THE CALLING ROUTINE HAS ALREADY ACQUIRED THE LOCK! Arguments: Lock - supplies structure to be torn down. The structure itself is not freed. Return Value: None. --*/ { HANDLE h1,h2; h1 = Lock->Handles[TABLE_DESTROYED_EVENT]; h2 = Lock->Handles[TABLE_ACCESS_MUTEX]; Lock->Handles[TABLE_DESTROYED_EVENT] = NULL; Lock->Handles[TABLE_ACCESS_MUTEX] = NULL; CloseHandle(h2); SetEvent(h1); CloseHandle(h1); } VOID pSetupCenterWindowRelativeToParent( HWND hwnd ) /*++ Routine Description: Centers a dialog relative to its owner, taking into account the 'work area' of the desktop. Arguments: hwnd - window handle of dialog to center Return Value: None. --*/ { RECT rcFrame, rcWindow; LONG x, y, w, h; POINT point; HWND Parent; Parent = GetWindow(hwnd,GW_OWNER); if(Parent == NULL) { return; } point.x = point.y = 0; ClientToScreen(Parent,&point); GetWindowRect(hwnd,&rcWindow); GetClientRect(Parent,&rcFrame); w = rcWindow.right - rcWindow.left + 1; h = rcWindow.bottom - rcWindow.top + 1; x = point.x + ((rcFrame.right - rcFrame.left + 1 - w) / 2); y = point.y + ((rcFrame.bottom - rcFrame.top + 1 - h) / 2); // // Get the work area for the current desktop (i.e., the area that // the tray doesn't occupy). // if(!SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rcFrame, 0)) { // // For some reason SPI failed, so use the full screen. // rcFrame.top = rcFrame.left = 0; rcFrame.right = GetSystemMetrics(SM_CXSCREEN); rcFrame.bottom = GetSystemMetrics(SM_CYSCREEN); } if(x + w > rcFrame.right) { x = rcFrame.right - w; } else if(x < rcFrame.left) { x = rcFrame.left; } if(y + h > rcFrame.bottom) { y = rcFrame.bottom - h; } else if(y < rcFrame.top) { y = rcFrame.top; } MoveWindow(hwnd,x,y,w,h,FALSE); } #endif // !SPUTILSW