Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

356 lines
11 KiB

  1. /*++
  2. Copyright (c) 2000, 2001 Microsoft Corporation
  3. Module Name:
  4. AppAndCommandLine.cpp
  5. Abstract:
  6. This class takes an application name and command line and
  7. parses them *exactly* as as they would by CreateProcess.
  8. If the Set routine returns TRUE, then the application name
  9. will contain a value; however, it does not guarantee that the application
  10. actually exists.
  11. Example:
  12. AppAndCommandLine.Set(NULL, "notepad.exe readme.txt");
  13. GetApplicationName() == "notepad.exe"
  14. GetCommandline() == "notepad.exe readme.txt"
  15. GetCommandlineNoAppName() == "readme.txt"
  16. Notes:
  17. None
  18. History:
  19. 07/21/2000 robkenny Created
  20. 12/18/2000 prashkud Modified GetAppAndCommandLine to fix the AV
  21. caused by EmulateGetCommandLine when the
  22. layer was applied to Oregon Trail 4th Edition.
  23. 03/04/2001 robkenny Converted to use CString
  24. 03/29/2001 prashkud Modified GetAppnameAndCommandline to set
  25. the application name even when there is only
  26. Commandline passed and the application name
  27. passed is NULL.
  28. 08/14/2001 robkenny Moved code inside the ShimLib namespace.
  29. --*/
  30. #include "ShimLib.h"
  31. #include "StrSafe.h"
  32. namespace ShimLib
  33. {
  34. AppAndCommandLine::AppAndCommandLine(const char * applicationName, const char * commandLine)
  35. {
  36. CString csApp(applicationName);
  37. CString csCl(commandLine);
  38. GetAppnameAndCommandline(csApp.GetNIE(), csCl.GetNIE());
  39. }
  40. AppAndCommandLine::AppAndCommandLine(const WCHAR * applicationName, const WCHAR * commandLine)
  41. {
  42. GetAppnameAndCommandline(applicationName, commandLine);
  43. }
  44. // If the application name is the first entry on the command line,
  45. // we convert the appName to its short name; thereby removing any spaces.
  46. const CString & AppAndCommandLine::GetShortCommandLine()
  47. {
  48. // If lpCommandLineNoAppName is not the same as lpCommandLine,
  49. // then the command line contains the application name.
  50. if ( csShortCommandLine.IsEmpty() && // Haven't been here
  51. !csApplicationName.IsEmpty() && // Set() has been called
  52. !csCommandLine.IsEmpty() && // Set() has been called
  53. csShortCommandLine.GetLength() != csCommandLine.GetLength()) // Command line actually contains app name
  54. {
  55. csShortCommandLine = csApplicationName;
  56. csShortCommandLine.GetShortPathNameW();
  57. csShortCommandLine += L" ";
  58. csShortCommandLine += csCommandLineNoAppName;
  59. }
  60. // If we still don't have a short version of the command line,
  61. // just duplicate the current command line
  62. if (csShortCommandLine.IsEmpty())
  63. {
  64. csShortCommandLine = csCommandLine;
  65. }
  66. return csShortCommandLine;
  67. }
  68. BOOL AppAndCommandLine::GetAppnameAndCommandline(const WCHAR * lpcApp, const WCHAR * lpcCl)
  69. {
  70. BOOL SearchRetry = TRUE;
  71. ULONG Length = 0;
  72. WCHAR * NameBuffer = NULL;
  73. BOOL success = FALSE;
  74. DWORD dwAppNameLen = 0;
  75. CString csTempAppName;
  76. BOOL bFound = TRUE;
  77. // It is really, really bad to remove the const from the Get,
  78. // However we never change the length of the string, so it should be okay
  79. WCHAR * lpApplicationName = (WCHAR *)lpcApp;
  80. WCHAR * lpCommandLine = (WCHAR *)lpcCl;
  81. // The following is done as there are lot of instances when the
  82. // the pointer is not NULL but contains an EMPTY string.
  83. if (lpApplicationName && !(*lpApplicationName))
  84. {
  85. lpApplicationName = NULL;
  86. }
  87. if (lpCommandLine && !(*lpCommandLine))
  88. {
  89. lpCommandLine = NULL;
  90. }
  91. if (lpApplicationName == NULL && lpCommandLine == NULL)
  92. {
  93. // Degenerate case
  94. csApplicationName = L"";
  95. csCommandLine = csApplicationName;
  96. csCommandLineNoAppName = csApplicationName;
  97. return FALSE; // Didn't find application name
  98. }
  99. csCommandLine = lpCommandLine;
  100. DPF("Common",
  101. eDbgLevelSpew,
  102. "[AppAndCommandLineT::Set] BEFORE App(%S) CL(%S)\n",
  103. lpApplicationName, lpCommandLine);
  104. if (lpApplicationName == NULL)
  105. {
  106. DWORD fileattr;
  107. WCHAR TempChar;
  108. //
  109. // Locate the image
  110. //
  111. NameBuffer = (WCHAR *) malloc(MAX_PATH * sizeof( WCHAR ));
  112. if ( !NameBuffer )
  113. {
  114. goto errorExit;
  115. }
  116. lpApplicationName = lpCommandLine;
  117. WCHAR * TempNull = lpApplicationName;
  118. WCHAR * WhiteScan = lpApplicationName;
  119. DWORD QuoteFound = 0;
  120. //
  121. // check for lead quote
  122. //
  123. if ( *WhiteScan == L'\"' ) {
  124. SearchRetry = FALSE;
  125. WhiteScan++;
  126. lpApplicationName = WhiteScan;
  127. while(*WhiteScan) {
  128. if ( *WhiteScan == L'\"' ) {
  129. TempNull = WhiteScan;
  130. QuoteFound = 2;
  131. break;
  132. }
  133. WhiteScan++;
  134. TempNull = WhiteScan;
  135. }
  136. }
  137. else {
  138. retrywsscan:
  139. lpApplicationName = lpCommandLine;
  140. while(*WhiteScan) {
  141. if ( *WhiteScan == L' ' ||
  142. *WhiteScan == L'\t' ) {
  143. TempNull = WhiteScan;
  144. break;
  145. }
  146. WhiteScan++;
  147. TempNull = WhiteScan;
  148. }
  149. }
  150. TempChar = *TempNull;
  151. *TempNull = 0;
  152. WCHAR * filePart;
  153. Length = SearchPathW(
  154. NULL,
  155. lpApplicationName,
  156. L".exe",
  157. MAX_PATH,
  158. NameBuffer,
  159. &filePart
  160. )*sizeof(WCHAR);
  161. if (Length != 0 && Length < MAX_PATH * sizeof( WCHAR )) {
  162. //
  163. // SearchPathW worked, but file might be a directory
  164. // if this happens, we need to keep trying
  165. //
  166. fileattr = GetFileAttributesW(NameBuffer);
  167. if ( fileattr != 0xffffffff &&
  168. (fileattr & FILE_ATTRIBUTE_DIRECTORY) ) {
  169. Length = 0;
  170. } else {
  171. Length++;
  172. Length++;
  173. }
  174. }
  175. if ( !Length || Length >= MAX_PATH<<1 ) {
  176. //
  177. // restore the command line
  178. //
  179. *TempNull = TempChar;
  180. lpApplicationName = lpCommandLine;
  181. //
  182. // If we still have command line left, then keep going
  183. // the point is to march through the command line looking
  184. // for whitespace so we can try to find an image name
  185. // launches of things like:
  186. // c:\word 95\winword.exe /embedding -automation
  187. // require this. Our first iteration will stop at c:\word, our next
  188. // will stop at c:\word 95\winword.exe
  189. //
  190. if (*WhiteScan && SearchRetry) {
  191. WhiteScan++;
  192. TempNull = WhiteScan;
  193. goto retrywsscan;
  194. }
  195. // If we are here then the Application has not been found.
  196. // We used to send back lpApplicationName as NULL earlier
  197. // but now instead we fill the ApplicationName with the
  198. // commandline till the first space or tab.This was added
  199. // to support EmulateMissingExe SHIM which will fail if
  200. // we return NULL as we used to earlier.
  201. bFound = FALSE;
  202. if (QuoteFound == 0)
  203. {
  204. // No quotes were found.
  205. lpApplicationName = lpCommandLine;
  206. // Since we just reset to the entire command line, we need to skip leading white space
  207. SkipBlanksW(lpApplicationName);
  208. TempNull = lpApplicationName;
  209. while (*TempNull)
  210. {
  211. if ((*TempNull == L' ') ||
  212. (*TempNull == L'\t') )
  213. {
  214. TempChar = *TempNull;
  215. *TempNull = 0;
  216. break;
  217. }
  218. TempNull++;
  219. }
  220. }
  221. else
  222. {
  223. // Quotes were found.
  224. lpApplicationName = lpCommandLine + 1; // Skip leading quote.
  225. *TempNull = 0;
  226. }
  227. csTempAppName = lpApplicationName;
  228. *TempNull = TempChar;
  229. dwAppNameLen = (DWORD)(TempNull - lpCommandLine) + QuoteFound;
  230. lpApplicationName = (WCHAR*)csTempAppName.Get();
  231. goto successExit;
  232. }
  233. dwAppNameLen = (DWORD)(TempNull - lpApplicationName) + QuoteFound;
  234. //
  235. // restore the command line
  236. //
  237. *TempNull = TempChar;
  238. lpApplicationName = NameBuffer;
  239. }
  240. else if (lpCommandLine == NULL || *lpCommandLine == 0 )
  241. {
  242. lpCommandLine = lpApplicationName;
  243. dwAppNameLen = wcslen(lpApplicationName);
  244. }
  245. // If they provided both, check to see if the app name
  246. // is the first entry on the command line.
  247. else if (lpApplicationName != NULL && lpCommandLine != NULL )
  248. {
  249. int appNameLen = wcslen(lpApplicationName);
  250. if (
  251. _wcsnicmp(lpApplicationName, lpCommandLine, appNameLen) == 0 &&
  252. (lpCommandLine[appNameLen] == 0 || iswspace(lpCommandLine[appNameLen]))
  253. )
  254. {
  255. // lpApplicationName is the first entry on the command line
  256. dwAppNameLen = appNameLen;
  257. }
  258. // check for quoted lpApplicationName
  259. else if (
  260. lpCommandLine[0] == L'"' &&
  261. _wcsnicmp(lpApplicationName, lpCommandLine+1, appNameLen) == 0 &&
  262. lpCommandLine[appNameLen+1] == L'"' &&
  263. (lpCommandLine[appNameLen+2] == 0 || iswspace(lpCommandLine[appNameLen+2]))
  264. )
  265. {
  266. // lpApplicationName is the first *quoted* entry on the command line
  267. dwAppNameLen = appNameLen + 2;
  268. }
  269. else
  270. {
  271. // Didn't find the application name at the beginning of the command line
  272. dwAppNameLen = 0;
  273. }
  274. }
  275. successExit:
  276. if (bFound)
  277. {
  278. success = TRUE;
  279. }
  280. csApplicationName = lpApplicationName;
  281. csCommandLineNoAppName = lpCommandLine + dwAppNameLen;
  282. csCommandLineNoAppName.TrimLeft();
  283. errorExit:
  284. free(NameBuffer);
  285. DPF("Common",
  286. eDbgLevelSpew,
  287. "[AppAndCommandLineT::Set] AFTER App(%S) CL(%S)\n",
  288. csApplicationName.Get(), csCommandLine.Get());
  289. DPF("Common",
  290. eDbgLevelSpew,
  291. "[AppAndCommandLineT::Set] CL without App(%S)\n",
  292. csCommandLineNoAppName.Get());
  293. return success;
  294. }
  295. }; // end of namespace ShimLib