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.

677 lines
18 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. start.c
  5. Abstract:
  6. Start command support
  7. --*/
  8. #include "cmd.h"
  9. extern UINT CurrentCP;
  10. extern unsigned DosErr;
  11. extern TCHAR CurDrvDir[] ;
  12. extern TCHAR SwitChar, PathChar;
  13. extern TCHAR ComExt[], ComSpecStr[];
  14. extern struct envdata * penvOrig;
  15. extern int LastRetCode;
  16. WORD
  17. GetProcessSubsystemType(
  18. HANDLE hProcess
  19. );
  20. STATUS
  21. getparam(
  22. IN BOOL LeadingSwitChar,
  23. IN OUT TCHAR **chptr,
  24. OUT TCHAR *param,
  25. IN int maxlen )
  26. /*++
  27. Routine Description:
  28. Copy the token starting at the current position to an output buffer.
  29. Terminate the copy on end-of-quotes, unquoted whitespace, unquoted
  30. switch character, or end-of-line
  31. Arguments:
  32. LeadingSwitChar - TRUE => we should terminate on unquoted switch character
  33. chptr - address of pointer to token. This is advanced over the parsed
  34. token
  35. param - destination of copy. String is NUL terminated
  36. maxlen - size of destination buffer
  37. Return Value:
  38. Return: STATUS of copy. Only failure is buffer exceeded.
  39. --*/
  40. {
  41. TCHAR *ch2;
  42. int count = 0;
  43. BOOL QuoteFound = FALSE;
  44. ch2 = param;
  45. //
  46. // get characters until a space, tab, slash, or end of line
  47. //
  48. while (TRUE) {
  49. //
  50. // If we're at the end of string, then there's no more token
  51. //
  52. if (**chptr == NULLC) {
  53. break;
  54. }
  55. //
  56. // If we're not quoting and we're at a whitespace or switch char
  57. // then there's no more token
  58. //
  59. if (!QuoteFound &&
  60. (_istspace( **chptr ) || (LeadingSwitChar && **chptr == SwitChar))) {
  61. break;
  62. }
  63. //
  64. // If there's still room in the buffer, copy in the character and note
  65. // if it's a quote or not
  66. //
  67. if (count < maxlen) {
  68. *ch2++ = (**chptr);
  69. if (**chptr == QUOTE) {
  70. QuoteFound = !QuoteFound;
  71. }
  72. }
  73. //
  74. // Advance over this character
  75. //
  76. (*chptr)++;
  77. count++;
  78. }
  79. //
  80. // If we've exceeded the buffer, display the error and return failure
  81. //
  82. if (count > maxlen) {
  83. **chptr = NULLC;
  84. *chptr = *chptr - count - 1;
  85. PutStdErr(MSG_START_INVALID_PARAMETER, ONEARG, *chptr);
  86. return(FAILURE);
  87. } else {
  88. *ch2 = NULLC;
  89. return(SUCCESS);
  90. }
  91. }
  92. /*
  93. Start /MIN /MAX "title" /P:x,y /S:dx,dy /D:directory /I cmd args
  94. */
  95. int
  96. Start(
  97. IN PTCHAR pszCmdLine
  98. )
  99. {
  100. STARTUPINFO StartupInfo;
  101. PROCESS_INFORMATION ChildProcessInfo;
  102. #ifndef UNICODE
  103. WCHAR TitleW[MAXTOKLEN];
  104. CCHAR TitleA[MAXTOKLEN];
  105. #endif
  106. TCHAR szTitle[MAXTOKLEN];
  107. TCHAR szDirCur[MAX_PATH];
  108. TCHAR szT[MAXTOKLEN];
  109. TCHAR szPgmArgs[MAXTOKLEN];
  110. TCHAR szParam[MAXTOKLEN];
  111. TCHAR szPgm[MAXTOKLEN];
  112. TCHAR szPgmSave[MAXTOKLEN];
  113. TCHAR szTemp[MAXTOKLEN];
  114. TCHAR szPgmQuoted[MAXTOKLEN];
  115. HDESK hdesk;
  116. HWINSTA hwinsta;
  117. LPTSTR p;
  118. LPTSTR lpDesktop;
  119. DWORD cbDesktop = 0;
  120. DWORD cbWinsta = 0;
  121. TCHAR flags;
  122. BOOLEAN fNeedCmd;
  123. BOOLEAN fNeedExpl;
  124. BOOLEAN fKSwitch = FALSE;
  125. BOOLEAN fCSwitch = FALSE;
  126. PTCHAR pszCmdCur = NULL;
  127. PTCHAR pszDirCur = NULL;
  128. PTCHAR pszPgmArgs = NULL;
  129. PTCHAR pszEnv = NULL;
  130. TCHAR pszFakePgm[] = TEXT("cmd.exe");
  131. ULONG status;
  132. struct cmdnode cmdnd;
  133. DWORD CreationFlags;
  134. BOOL SafeFromControlC = FALSE;
  135. BOOL WaitForProcess = FALSE;
  136. BOOL b;
  137. DWORD uPgmLength;
  138. int retc;
  139. szPgm[0] = NULLC;
  140. szPgmArgs[0] = NULLC;
  141. pszDirCur = NULL;
  142. CreationFlags = CREATE_NEW_CONSOLE;
  143. StartupInfo.cb = sizeof( StartupInfo );
  144. StartupInfo.lpReserved = NULL;
  145. StartupInfo.lpDesktop = NULL;
  146. StartupInfo.lpTitle = NULL;
  147. StartupInfo.dwX = 0;
  148. StartupInfo.dwY = 0;
  149. StartupInfo.dwXSize = 0;
  150. StartupInfo.dwYSize = 0;
  151. StartupInfo.dwFlags = 0;
  152. StartupInfo.wShowWindow = SW_SHOWNORMAL;
  153. StartupInfo.cbReserved2 = 0;
  154. StartupInfo.lpReserved2 = NULL;
  155. StartupInfo.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
  156. StartupInfo.hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE );
  157. StartupInfo.hStdError = GetStdHandle( STD_ERROR_HANDLE );
  158. pszCmdCur = pszCmdLine;
  159. //
  160. // If there isn't a command line then make
  161. // up the default
  162. //
  163. if (pszCmdCur == NULL) {
  164. pszCmdCur = pszFakePgm;
  165. }
  166. while( *pszCmdCur != NULLC) {
  167. pszCmdCur = EatWS( pszCmdCur, NULL );
  168. if ((*pszCmdCur == QUOTE) && (StartupInfo.lpTitle == NULL)) {
  169. //
  170. // "Title" Parse off the quoted text, strip off quotes and set the
  171. // title for the child window.
  172. //
  173. if (getparam( TRUE, &pszCmdCur, szTitle, sizeof( szTitle )) == FAILURE) {
  174. return FAILURE;
  175. }
  176. mystrcpy( szTitle, StripQuotes( szTitle ));
  177. StartupInfo.lpTitle = szTitle;
  178. } else if (*pszCmdCur == SwitChar) {
  179. pszCmdCur++;
  180. if (getparam( TRUE, &pszCmdCur, szParam, MAXTOKLEN) == FAILURE) {
  181. return(FAILURE);
  182. }
  183. if (!_tcsicmp( szParam, TEXT("ABOVENORMAL"))) {
  184. CreationFlags |= ABOVE_NORMAL_PRIORITY_CLASS;
  185. } else
  186. if (!_tcsicmp( szParam, TEXT("BELOWNORMAL"))) {
  187. CreationFlags |= BELOW_NORMAL_PRIORITY_CLASS;
  188. } else
  189. if (!_tcsicmp( szParam, TEXT("B"))) {
  190. WaitForProcess = FALSE;
  191. SafeFromControlC = TRUE;
  192. CreationFlags &= ~CREATE_NEW_CONSOLE;
  193. CreationFlags |= CREATE_NEW_PROCESS_GROUP;
  194. } else
  195. if (_totupper(szParam[0]) == TEXT('D')) {
  196. //
  197. // /Dpath or /D"path" or /D path or /D "path"
  198. //
  199. if (mystrlen( szParam + 1 ) > 0) {
  200. //
  201. // /Dpath or /D"path"
  202. //
  203. pszDirCur = szParam + 1;
  204. } else {
  205. //
  206. // /D path or /D "path"
  207. //
  208. pszCmdCur = EatWS( pszCmdCur, NULL );
  209. if (getparam( TRUE, &pszCmdCur, szParam, MAXTOKLEN) == FAILURE) {
  210. return FAILURE;
  211. }
  212. pszDirCur = szParam;
  213. }
  214. //
  215. // remove quotes if necessary
  216. //
  217. mystrcpy( szDirCur, StripQuotes( pszDirCur ));
  218. pszDirCur = szDirCur;
  219. if (mystrlen( pszDirCur ) > MAX_PATH) {
  220. PutStdErr( MSG_START_INVALID_PARAMETER, ONEARG, pszDirCur);
  221. return FAILURE;
  222. }
  223. } else
  224. if (_tcsicmp(szParam, TEXT("HIGH")) == 0) {
  225. CreationFlags |= HIGH_PRIORITY_CLASS;
  226. } else
  227. if (_totupper(szParam[0]) == TEXT('I')) {
  228. //
  229. // penvOrig was save at init time after path
  230. // and compsec were setup.
  231. // If penvOrig did not get allocated then
  232. // use the default.
  233. //
  234. if (penvOrig) {
  235. pszEnv = penvOrig->handle;
  236. }
  237. } else
  238. if (_totupper(szParam[0]) == QMARK) {
  239. BeginHelpPause();
  240. PutStdOut(MSG_HELP_START, NOARGS);
  241. if (fEnableExtensions)
  242. PutStdOut(MSG_HELP_START_X, NOARGS);
  243. EndHelpPause();
  244. return( FAILURE );
  245. } else
  246. if (_tcsicmp(szParam, TEXT("LOW")) == 0) {
  247. CreationFlags |= IDLE_PRIORITY_CLASS;
  248. } else
  249. if (_tcsicmp(szParam, TEXT("MIN")) == 0) {
  250. StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
  251. StartupInfo.wShowWindow &= ~SW_SHOWNORMAL;
  252. StartupInfo.wShowWindow |= SW_SHOWMINNOACTIVE;
  253. } else
  254. if (_tcsicmp(szParam, TEXT("MAX")) == 0) {
  255. StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
  256. StartupInfo.wShowWindow &= ~SW_SHOWNORMAL;
  257. StartupInfo.wShowWindow |= SW_SHOWMAXIMIZED;
  258. } else
  259. if (_tcsicmp(szParam, TEXT("NORMAL")) == 0) {
  260. CreationFlags |= NORMAL_PRIORITY_CLASS;
  261. } else
  262. if (_tcsicmp(szParam, TEXT("REALTIME")) == 0) {
  263. CreationFlags |= REALTIME_PRIORITY_CLASS;
  264. } else
  265. if (_tcsicmp(szParam, TEXT("SEPARATE")) == 0) {
  266. #ifndef WIN95_CMD
  267. CreationFlags |= CREATE_SEPARATE_WOW_VDM;
  268. #endif // WIN95_CMD
  269. } else
  270. if (_tcsicmp(szParam, TEXT("SHARED")) == 0) {
  271. #ifndef WIN95_CMD
  272. CreationFlags |= CREATE_SHARED_WOW_VDM;
  273. #endif // WIN95_CMD
  274. } else
  275. if ( _tcsicmp(szParam, TEXT("WAIT")) == 0 ||
  276. _tcsicmp(szParam, TEXT("W")) == 0 ) {
  277. WaitForProcess = TRUE;
  278. } else {
  279. #ifdef FE_SB // KKBUGFIX
  280. mystrcpy(szT, TEXT("/"));
  281. #else
  282. mystrcpy(szT, TEXT("\\"));
  283. #endif
  284. mystrcat(szT, szParam );
  285. PutStdErr(MSG_INVALID_SWITCH, ONEARG, szT);
  286. return( FAILURE );
  287. }
  288. } else {
  289. if ((getparam(FALSE,&pszCmdCur,szPgm,MAXTOKLEN)) == FAILURE) {
  290. return( FAILURE );
  291. }
  292. //
  293. // if there are argument get them.
  294. //
  295. if (*pszCmdCur) {
  296. mystrcpy(szPgmArgs, pszCmdCur);
  297. pszPgmArgs = szPgmArgs;
  298. }
  299. //
  300. // there rest was args to pgm so move to eol
  301. //
  302. pszCmdCur = mystrchr(pszCmdCur, NULLC);
  303. }
  304. } // while
  305. //
  306. // If a program was not picked up do so now.
  307. //
  308. if (*szPgm == NULLC) {
  309. mystrcpy(szPgm, pszFakePgm);
  310. }
  311. //
  312. // Need both quoted and unquoted versions of program name
  313. //
  314. if (szPgm[0] != QUOTE && _tcschr(szPgm, SPACE)) {
  315. szPgmQuoted[0] = QUOTE;
  316. mystrcpy(&szPgmQuoted[1], StripQuotes(szPgm));
  317. mystrcat(szPgmQuoted, TEXT("\""));
  318. }
  319. else {
  320. mystrcpy(szPgmQuoted, szPgm);
  321. mystrcpy(szPgm, StripQuotes(szPgm));
  322. }
  323. #ifndef UNICODE
  324. #ifndef WIN95_CMD
  325. // convert the title from OEM to ANSI
  326. if (StartupInfo.lpTitle) {
  327. MultiByteToWideChar(CP_OEMCP,
  328. 0,
  329. StartupInfo.lpTitle,
  330. _tcslen(StartupInfo.lpTitle)+1,
  331. TitleW,
  332. MAXTOKLEN);
  333. WideCharToMultiByte(CP_ACP,
  334. 0,
  335. TitleW,
  336. wcslen(TitleW)+1,
  337. TitleA,
  338. MAXTOKLEN,
  339. NULL,
  340. NULL);
  341. StartupInfo.lpTitle = TitleA;
  342. }
  343. #endif // WIN95_CMD
  344. #endif // UNICODE
  345. //
  346. // see of a cmd.exe is needed to run a batch or internal command
  347. //
  348. fNeedCmd = FALSE;
  349. fNeedExpl = FALSE;
  350. //
  351. // is it an internal command?
  352. //
  353. if (FindCmd(CMDMAX, szPgm, &flags) != -1) {
  354. fNeedCmd = TRUE;
  355. } else {
  356. // Save szPgm since SearchForExecutable may override it.
  357. mystrcpy(szPgmSave, szPgm);
  358. //
  359. // Try to find it as a batch or exe file
  360. //
  361. cmdnd.cmdline = szPgm;
  362. status = SearchForExecutable(&cmdnd, szPgm);
  363. if ( (status == SFE_NOTFND) || ( status == SFE_FAIL ) ) {
  364. //
  365. // If we can find it, let Explorer have a shot.
  366. //
  367. fNeedExpl = TRUE;
  368. mystrcpy(szPgm, szPgmSave);
  369. } else if (status == SFE_ISBAT || status == SFE_ISDIR) {
  370. if (status == SFE_ISBAT)
  371. fNeedCmd = TRUE;
  372. else
  373. fNeedExpl = TRUE;
  374. }
  375. }
  376. if (!fNeedExpl) {
  377. if (fNeedCmd) {
  378. TCHAR *Cmd = GetEnvVar( ComSpecStr );
  379. if (Cmd == NULL) {
  380. PutStdErr( MSG_INVALID_COMSPEC, NOARGS );
  381. return FAILURE;
  382. }
  383. //
  384. // if a cmd.exe is need then szPgm need to be inserted before
  385. // the start of szPgms along with a /K parameter.
  386. // szPgm has to recieve the full path name of cmd.exe from
  387. // the compsec environment variable.
  388. //
  389. mystrcpy(szT, TEXT(" /K "));
  390. mystrcat(szT, szPgmQuoted);
  391. //
  392. // Get the location of the cmd processor from the environment
  393. //
  394. mystrcpy( szPgm, Cmd );
  395. mystrcpy( szPgmQuoted, szPgm );
  396. //
  397. // is there a command parameter at all
  398. //
  399. if (_tcsicmp(szT, TEXT(" /K ")) != 0) {
  400. //
  401. // If we have any arguments to add do so
  402. //
  403. if (*szPgmArgs) {
  404. if ((mystrlen(szPgmArgs) + mystrlen(szT)) < MAXTOKLEN) {
  405. mystrcat(szT, TEXT(" "));
  406. mystrcat(szT, szPgmArgs);
  407. } else {
  408. PutStdErr( MSG_CMD_FILE_NOT_FOUND, ONEARG, szPgmArgs);
  409. }
  410. }
  411. }
  412. pszPgmArgs = szT;
  413. }
  414. // Prepare for CreateProcess :
  415. // ImageName = <full path and command name ONLY>
  416. // CmdLine = <command name with NO FULL PATH> + <args as entered>
  417. mystrcpy(szTemp, szPgmQuoted);
  418. mystrcat(szTemp, TEXT(" "));
  419. mystrcat(szTemp, pszPgmArgs);
  420. pszPgmArgs = szTemp;
  421. }
  422. if (SafeFromControlC) {
  423. SetConsoleCtrlHandler(NULL,TRUE);
  424. }
  425. // Pass current Desktop to a new process.
  426. hwinsta = GetProcessWindowStation();
  427. GetUserObjectInformation( hwinsta, UOI_NAME, NULL, 0, &cbWinsta );
  428. hdesk = GetThreadDesktop ( GetCurrentThreadId() );
  429. GetUserObjectInformation (hdesk, UOI_NAME, NULL, 0, &cbDesktop);
  430. if ((lpDesktop = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, cbDesktop + cbWinsta + 32) ) != NULL ) {
  431. p = lpDesktop;
  432. if ( GetUserObjectInformation (hwinsta, UOI_NAME, p, cbWinsta, &cbWinsta) ) {
  433. if (cbWinsta > 0) {
  434. p += ((cbWinsta/sizeof(TCHAR))-1);
  435. *p++ = L'\\';
  436. }
  437. if ( GetUserObjectInformation (hdesk, UOI_NAME, p, cbDesktop, &cbDesktop) ) {
  438. StartupInfo.lpDesktop = lpDesktop;
  439. }
  440. }
  441. }
  442. if (fNeedExpl) {
  443. b = FALSE;
  444. } else {
  445. b = CreateProcess( szPgm, // was NULL, wrong.
  446. pszPgmArgs,
  447. NULL,
  448. (LPSECURITY_ATTRIBUTES) NULL,
  449. TRUE, // bInherit
  450. #ifdef UNICODE
  451. CREATE_UNICODE_ENVIRONMENT |
  452. #endif // UNICODE
  453. CreationFlags,
  454. // CreationFlags
  455. pszEnv, // Environment
  456. pszDirCur, // Current directory
  457. &StartupInfo, // Startup Info Struct
  458. &ChildProcessInfo // ProcessInfo Struct
  459. );
  460. }
  461. if (SafeFromControlC) {
  462. SetConsoleCtrlHandler(NULL,FALSE);
  463. }
  464. HeapFree (GetProcessHeap(), 0, lpDesktop);
  465. if (!b) {
  466. DosErr = GetLastError();
  467. if ( fNeedExpl ||
  468. (fEnableExtensions && DosErr == ERROR_BAD_EXE_FORMAT)) {
  469. SHELLEXECUTEINFO sei;
  470. BOOL b;
  471. memset(&sei, 0, sizeof(sei));
  472. //
  473. // Use the DDEWAIT flag so apps can finish their DDE conversation
  474. // before ShellExecuteEx returns. Otherwise, apps like Word will
  475. // complain when they try to exit, confusing the user.
  476. //
  477. sei.cbSize = sizeof(sei);
  478. sei.fMask = SEE_MASK_HASTITLE |
  479. SEE_MASK_NO_CONSOLE |
  480. SEE_MASK_FLAG_DDEWAIT |
  481. SEE_MASK_NOCLOSEPROCESS;
  482. if (CreationFlags & CREATE_NEW_CONSOLE) {
  483. sei.fMask &= ~SEE_MASK_NO_CONSOLE;
  484. }
  485. sei.lpFile = szPgm;
  486. sei.lpClass = StartupInfo.lpTitle;
  487. sei.lpParameters = szPgmArgs;
  488. sei.lpDirectory = pszDirCur;
  489. sei.nShow = StartupInfo.wShowWindow;
  490. try {
  491. b = ShellExecuteEx( &sei );
  492. if (b) {
  493. leave;
  494. }
  495. if (!sei.hInstApp) {
  496. DosErr = ERROR_NOT_ENOUGH_MEMORY;
  497. } else if ((DWORD_PTR)sei.hInstApp == HINSTANCE_ERROR) {
  498. DosErr = ERROR_FILE_NOT_FOUND;
  499. } else {
  500. DosErr = HandleToUlong(sei.hInstApp);
  501. }
  502. } except (DosErr = GetExceptionCode( ), EXCEPTION_EXECUTE_HANDLER) {
  503. b = FALSE;
  504. }
  505. if (b) {
  506. //
  507. // Successfully invoked correct application via
  508. // file association. Code below will check to see
  509. // if application is a GUI app and if so turn it into
  510. // an ASYNC exec.
  511. ChildProcessInfo.hProcess = sei.hProcess;
  512. goto shellexecsuccess;
  513. }
  514. }
  515. ExecError( szPgm ) ;
  516. return(FAILURE) ;
  517. }
  518. CloseHandle(ChildProcessInfo.hThread);
  519. shellexecsuccess:
  520. if (ChildProcessInfo.hProcess != NULL) {
  521. if (WaitForProcess) {
  522. //
  523. // Wait for process to terminate, otherwise things become very
  524. // messy and confusing to the user (with 2 processes sharing
  525. // the console).
  526. //
  527. LastRetCode = WaitProc((ChildProcessInfo.hProcess) );
  528. } else {
  529. CloseHandle( ChildProcessInfo.hProcess );
  530. }
  531. }
  532. return(SUCCESS);
  533. }