/*++ Copyright (c) 2000, 2001 Microsoft Corporation Module Name: AppAndCommandLine.cpp Abstract: This class takes an application name and command line and parses them *exactly* as as they would by CreateProcess. If the Set routine returns TRUE, then the application name will contain a value; however, it does not guarantee that the application actually exists. Example: AppAndCommandLine.Set(NULL, "notepad.exe readme.txt"); GetApplicationName() == "notepad.exe" GetCommandline() == "notepad.exe readme.txt" GetCommandlineNoAppName() == "readme.txt" Notes: None History: 07/21/2000 robkenny Created 12/18/2000 prashkud Modified GetAppAndCommandLine to fix the AV caused by EmulateGetCommandLine when the layer was applied to Oregon Trail 4th Edition. 03/04/2001 robkenny Converted to use CString 03/29/2001 prashkud Modified GetAppnameAndCommandline to set the application name even when there is only Commandline passed and the application name passed is NULL. 08/14/2001 robkenny Moved code inside the ShimLib namespace. --*/ #include "ShimLib.h" #include "StrSafe.h" namespace ShimLib { AppAndCommandLine::AppAndCommandLine(const char * applicationName, const char * commandLine) { CString csApp(applicationName); CString csCl(commandLine); GetAppnameAndCommandline(csApp.GetNIE(), csCl.GetNIE()); } AppAndCommandLine::AppAndCommandLine(const WCHAR * applicationName, const WCHAR * commandLine) { GetAppnameAndCommandline(applicationName, commandLine); } // If the application name is the first entry on the command line, // we convert the appName to its short name; thereby removing any spaces. const CString & AppAndCommandLine::GetShortCommandLine() { // If lpCommandLineNoAppName is not the same as lpCommandLine, // then the command line contains the application name. if ( csShortCommandLine.IsEmpty() && // Haven't been here !csApplicationName.IsEmpty() && // Set() has been called !csCommandLine.IsEmpty() && // Set() has been called csShortCommandLine.GetLength() != csCommandLine.GetLength()) // Command line actually contains app name { csShortCommandLine = csApplicationName; csShortCommandLine.GetShortPathNameW(); csShortCommandLine += L" "; csShortCommandLine += csCommandLineNoAppName; } // If we still don't have a short version of the command line, // just duplicate the current command line if (csShortCommandLine.IsEmpty()) { csShortCommandLine = csCommandLine; } return csShortCommandLine; } BOOL AppAndCommandLine::GetAppnameAndCommandline(const WCHAR * lpcApp, const WCHAR * lpcCl) { BOOL SearchRetry = TRUE; ULONG Length = 0; WCHAR * NameBuffer = NULL; BOOL success = FALSE; DWORD dwAppNameLen = 0; CString csTempAppName; BOOL bFound = TRUE; // It is really, really bad to remove the const from the Get, // However we never change the length of the string, so it should be okay WCHAR * lpApplicationName = (WCHAR *)lpcApp; WCHAR * lpCommandLine = (WCHAR *)lpcCl; // The following is done as there are lot of instances when the // the pointer is not NULL but contains an EMPTY string. if (lpApplicationName && !(*lpApplicationName)) { lpApplicationName = NULL; } if (lpCommandLine && !(*lpCommandLine)) { lpCommandLine = NULL; } if (lpApplicationName == NULL && lpCommandLine == NULL) { // Degenerate case csApplicationName = L""; csCommandLine = csApplicationName; csCommandLineNoAppName = csApplicationName; return FALSE; // Didn't find application name } csCommandLine = lpCommandLine; DPF("Common", eDbgLevelSpew, "[AppAndCommandLineT::Set] BEFORE App(%S) CL(%S)\n", lpApplicationName, lpCommandLine); if (lpApplicationName == NULL) { DWORD fileattr; WCHAR TempChar; // // Locate the image // NameBuffer = (WCHAR *) malloc(MAX_PATH * sizeof( WCHAR )); if ( !NameBuffer ) { goto errorExit; } lpApplicationName = lpCommandLine; WCHAR * TempNull = lpApplicationName; WCHAR * WhiteScan = lpApplicationName; DWORD QuoteFound = 0; // // check for lead quote // if ( *WhiteScan == L'\"' ) { SearchRetry = FALSE; WhiteScan++; lpApplicationName = WhiteScan; while(*WhiteScan) { if ( *WhiteScan == L'\"' ) { TempNull = WhiteScan; QuoteFound = 2; break; } WhiteScan++; TempNull = WhiteScan; } } else { retrywsscan: lpApplicationName = lpCommandLine; while(*WhiteScan) { if ( *WhiteScan == L' ' || *WhiteScan == L'\t' ) { TempNull = WhiteScan; break; } WhiteScan++; TempNull = WhiteScan; } } TempChar = *TempNull; *TempNull = 0; WCHAR * filePart; Length = SearchPathW( NULL, lpApplicationName, L".exe", MAX_PATH, NameBuffer, &filePart )*sizeof(WCHAR); if (Length != 0 && Length < MAX_PATH * sizeof( WCHAR )) { // // SearchPathW worked, but file might be a directory // if this happens, we need to keep trying // fileattr = GetFileAttributesW(NameBuffer); if ( fileattr != 0xffffffff && (fileattr & FILE_ATTRIBUTE_DIRECTORY) ) { Length = 0; } else { Length++; Length++; } } if ( !Length || Length >= MAX_PATH<<1 ) { // // restore the command line // *TempNull = TempChar; lpApplicationName = lpCommandLine; // // If we still have command line left, then keep going // the point is to march through the command line looking // for whitespace so we can try to find an image name // launches of things like: // c:\word 95\winword.exe /embedding -automation // require this. Our first iteration will stop at c:\word, our next // will stop at c:\word 95\winword.exe // if (*WhiteScan && SearchRetry) { WhiteScan++; TempNull = WhiteScan; goto retrywsscan; } // If we are here then the Application has not been found. // We used to send back lpApplicationName as NULL earlier // but now instead we fill the ApplicationName with the // commandline till the first space or tab.This was added // to support EmulateMissingExe SHIM which will fail if // we return NULL as we used to earlier. bFound = FALSE; if (QuoteFound == 0) { // No quotes were found. lpApplicationName = lpCommandLine; // Since we just reset to the entire command line, we need to skip leading white space SkipBlanksW(lpApplicationName); TempNull = lpApplicationName; while (*TempNull) { if ((*TempNull == L' ') || (*TempNull == L'\t') ) { TempChar = *TempNull; *TempNull = 0; break; } TempNull++; } } else { // Quotes were found. lpApplicationName = lpCommandLine + 1; // Skip leading quote. *TempNull = 0; } csTempAppName = lpApplicationName; *TempNull = TempChar; dwAppNameLen = (DWORD)(TempNull - lpCommandLine) + QuoteFound; lpApplicationName = (WCHAR*)csTempAppName.Get(); goto successExit; } dwAppNameLen = (DWORD)(TempNull - lpApplicationName) + QuoteFound; // // restore the command line // *TempNull = TempChar; lpApplicationName = NameBuffer; } else if (lpCommandLine == NULL || *lpCommandLine == 0 ) { lpCommandLine = lpApplicationName; dwAppNameLen = wcslen(lpApplicationName); } // If they provided both, check to see if the app name // is the first entry on the command line. else if (lpApplicationName != NULL && lpCommandLine != NULL ) { int appNameLen = wcslen(lpApplicationName); if ( _wcsnicmp(lpApplicationName, lpCommandLine, appNameLen) == 0 && (lpCommandLine[appNameLen] == 0 || iswspace(lpCommandLine[appNameLen])) ) { // lpApplicationName is the first entry on the command line dwAppNameLen = appNameLen; } // check for quoted lpApplicationName else if ( lpCommandLine[0] == L'"' && _wcsnicmp(lpApplicationName, lpCommandLine+1, appNameLen) == 0 && lpCommandLine[appNameLen+1] == L'"' && (lpCommandLine[appNameLen+2] == 0 || iswspace(lpCommandLine[appNameLen+2])) ) { // lpApplicationName is the first *quoted* entry on the command line dwAppNameLen = appNameLen + 2; } else { // Didn't find the application name at the beginning of the command line dwAppNameLen = 0; } } successExit: if (bFound) { success = TRUE; } csApplicationName = lpApplicationName; csCommandLineNoAppName = lpCommandLine + dwAppNameLen; csCommandLineNoAppName.TrimLeft(); errorExit: free(NameBuffer); DPF("Common", eDbgLevelSpew, "[AppAndCommandLineT::Set] AFTER App(%S) CL(%S)\n", csApplicationName.Get(), csCommandLine.Get()); DPF("Common", eDbgLevelSpew, "[AppAndCommandLineT::Set] CL without App(%S)\n", csCommandLineNoAppName.Get()); return success; } }; // end of namespace ShimLib