Source code of Windows XP (NT5)
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.

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