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.

980 lines
25 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995
  5. //
  6. // File: process.cxx
  7. //
  8. // Contents: Implementation of the CProcessThread class
  9. //
  10. //----------------------------------------------------------------------------
  11. #include "headers.hxx"
  12. #include "stdlib.h"
  13. DeclareTag(tagProcess, "MTScript", "Monitor Process Creation");
  14. char *g_pszExitCodeStr = "PROCESS_EXIT_CODE=";
  15. #define PIPEREAD_TIMEOUT 2
  16. CProcessParams::CProcessParams()
  17. {
  18. pszCommand = 0;
  19. pszDir = 0;
  20. pszTitle = 0;
  21. }
  22. CProcessParams::~CProcessParams()
  23. {
  24. Free();
  25. }
  26. bool CProcessParams::Copy(const PROCESS_PARAMS *params)
  27. {
  28. return Assign(*params);
  29. }
  30. void CProcessParams::Free()
  31. {
  32. SysFreeString(pszCommand);
  33. SysFreeString(pszDir);
  34. SysFreeString(pszTitle);
  35. pszCommand = 0;
  36. pszDir = 0;
  37. pszTitle = 0;
  38. }
  39. bool CProcessParams::Assign(const PROCESS_PARAMS &params)
  40. {
  41. pszCommand = SysAllocString(params.pszCommand);
  42. pszDir = SysAllocString(params.pszDir);
  43. pszTitle = SysAllocString(params.pszTitle);
  44. fMinimize = params.fMinimize;
  45. fGetOutput = params.fGetOutput;
  46. fNoEnviron = params.fNoEnviron;
  47. fNoCrashPopup = params.fNoCrashPopup;
  48. if (params.pszCommand && !pszCommand ||
  49. params.pszDir && !pszDir ||
  50. params.pszTitle && !pszTitle)
  51. {
  52. return false;
  53. }
  54. return true;
  55. }
  56. //+---------------------------------------------------------------------------
  57. //
  58. // Member: CProcessThread::CProcessThread, public
  59. //
  60. // Synopsis: ctor
  61. //
  62. //----------------------------------------------------------------------------
  63. CProcessThread::CProcessThread(CScriptHost *pSH)
  64. : _cstrOutput(CSTR_NOINIT)
  65. {
  66. _ulRefs = 1;
  67. _pSH = pSH;
  68. _pSH->AddRef();
  69. // This object should be initialized to all zero.
  70. Assert(_hPipe == NULL);
  71. Assert(_hJob == NULL);
  72. Assert(_hIoPort == NULL);
  73. Assert(_dwExitCode == 0);
  74. Assert(_piProc.hProcess == NULL);
  75. }
  76. //+---------------------------------------------------------------------------
  77. //
  78. // Member: CProcessThread::~CProcessThread, public
  79. //
  80. // Synopsis: dtor
  81. //
  82. //----------------------------------------------------------------------------
  83. CProcessThread::~CProcessThread()
  84. {
  85. AssertSz(!_hJob, "NONFATAL: Job handle should be NULL!");
  86. AssertSz(!_hIoPort, "NONFATAL: Port handle should be NULL!");
  87. TraceTag((tagProcess, "Closing process PID=%d", ProcId()));
  88. if (_hPipe)
  89. CloseHandle(_hPipe);
  90. if (_piProc.hProcess)
  91. CloseHandle(_piProc.hProcess);
  92. ReleaseInterface(_pSH);
  93. }
  94. //+---------------------------------------------------------------------------
  95. //
  96. // Member: CProcessThread::QueryInterface, public
  97. //
  98. // Synopsis: Standard implementation. This class implements no meaningful
  99. // interfaces.
  100. //
  101. //----------------------------------------------------------------------------
  102. HRESULT
  103. CProcessThread::QueryInterface(REFIID iid, void **ppvObj)
  104. {
  105. if (iid == IID_IUnknown)
  106. {
  107. *ppvObj = (IUnknown *)this;
  108. }
  109. else
  110. {
  111. *ppvObj = NULL;
  112. return E_NOINTERFACE;
  113. }
  114. ((IUnknown *)*ppvObj)->AddRef();
  115. return S_OK;
  116. }
  117. //+---------------------------------------------------------------------------
  118. //
  119. // Member: CProcessThread::Init, public
  120. //
  121. // Synopsis: Initializes the class
  122. //
  123. //----------------------------------------------------------------------------
  124. BOOL
  125. CProcessThread::Init()
  126. {
  127. //$ FUTURE: For NT4 and Win9x support we will need to dynamically use the
  128. // job APIs since those platforms do not support them.
  129. _hJob = CreateJobObject(NULL, NULL);
  130. if (!_hJob)
  131. {
  132. TraceTag((tagError, "CreateJobObject failed with %d", GetLastError()));
  133. return FALSE;
  134. }
  135. return CThreadComm::Init();
  136. }
  137. //+---------------------------------------------------------------------------
  138. //
  139. // Member: CProcessThread::GetProcessOutput, public
  140. //
  141. // Synopsis: Returns the collected STDOUT/STDERR data from the process
  142. // up to this point.
  143. //
  144. // Arguments: [pbstrOutput] -- Pointer to BSTR where the string should be
  145. // copied. The BSTR will be allocated.
  146. //
  147. // Returns: HRESULT
  148. //
  149. //----------------------------------------------------------------------------
  150. HRESULT
  151. CProcessThread::GetProcessOutput(BSTR *pbstrOutput)
  152. {
  153. LOCK_LOCALS(this);
  154. return _cstrOutput.AllocBSTR(pbstrOutput);
  155. }
  156. //+---------------------------------------------------------------------------
  157. //
  158. // Member: CProcessThread::GetExitCode, public
  159. //
  160. // Synopsis: Returns the exit code of the process. If the process is still
  161. // running, it returns STILL_ACTIVE (0x103, dec 259)
  162. //
  163. //----------------------------------------------------------------------------
  164. DWORD
  165. CProcessThread::GetExitCode()
  166. {
  167. DWORD dwExit;
  168. AssertSz(_piProc.hProcess, "FATAL: Bad process handle");
  169. GetExitCodeProcess(_piProc.hProcess, &dwExit);
  170. if (dwExit == STILL_ACTIVE || !_fUseExitCode)
  171. {
  172. return STILL_ACTIVE;
  173. }
  174. return _dwExitCode;
  175. }
  176. //+---------------------------------------------------------------------------
  177. //
  178. // Member: CProcessThread::GetDeadTime, public
  179. //
  180. // Synopsis: Returns the number of ms since the process exited. If it
  181. // hasn't exited yet, it returns 0.
  182. //
  183. //----------------------------------------------------------------------------
  184. ULONG
  185. CProcessThread::GetDeadTime()
  186. {
  187. _int64 i64CurrentTime;
  188. DWORD dwExit;
  189. AssertSz(_piProc.hProcess, "FATAL: Bad process handle");
  190. GetExitCodeProcess(_piProc.hProcess, &dwExit);
  191. // If the process is still running return 0.
  192. if (dwExit == STILL_ACTIVE || !_i64ExitTime)
  193. {
  194. return 0;
  195. }
  196. GetSystemTimeAsFileTime((FILETIME*)&i64CurrentTime);
  197. // Calculate the difference in milli-seconds. There are 10,000
  198. // FILETIME intervals in one second (each increment of 1 represents 100
  199. // nano-seconds)
  200. return (i64CurrentTime - _i64ExitTime) / 10000;
  201. }
  202. //+---------------------------------------------------------------------------
  203. //
  204. // Member: CProcessThread::Terminate, public
  205. //
  206. // Synopsis: Exits this thread. The process we own is terminated if it's
  207. // still running.
  208. //
  209. //----------------------------------------------------------------------------
  210. void
  211. CProcessThread::Terminate()
  212. {
  213. DWORD dwCode;
  214. CProcessThread * pThis = this;
  215. BOOL fTerminated = FALSE;
  216. BOOL fFireEvent = FALSE;
  217. VERIFY_THREAD();
  218. TraceTag((tagProcess, "Entering Terminate"));
  219. //
  220. // Flush out any data the process may have written to the pipe.
  221. //
  222. ReadPipeData();
  223. //
  224. // Make sure we've received all messages from our completion port
  225. //
  226. CheckIoPort();
  227. if (_piProc.hProcess)
  228. {
  229. //
  230. // Terminate the process(es) if still running.
  231. //
  232. fFireEvent = TRUE;
  233. GetExitCodeProcess(_piProc.hProcess, &dwCode);
  234. if (dwCode == STILL_ACTIVE)
  235. {
  236. TraceTag((tagProcess, "Root process still active!"));
  237. if (_hJob)
  238. {
  239. TerminateJobObject(_hJob, ERROR_PROCESS_ABORTED);
  240. }
  241. else
  242. {
  243. TraceTag((tagProcess, "Terminating process, not job!"));
  244. TerminateProcess(_piProc.hProcess, ERROR_PROCESS_ABORTED);
  245. }
  246. dwCode = ERROR_PROCESS_ABORTED;
  247. //
  248. // Flush out any data the process may have written to the pipe.
  249. //
  250. ReadPipeData();
  251. //
  252. // Make sure we've received all messages from our completion port
  253. //
  254. CheckIoPort();
  255. fTerminated = TRUE;
  256. }
  257. if (!_fUseExitCode)
  258. {
  259. _dwExitCode = dwCode;
  260. _fUseExitCode = TRUE;
  261. }
  262. }
  263. if (_hIoPort)
  264. CloseHandle(_hIoPort);
  265. if (_hJob)
  266. CloseHandle(_hJob);
  267. _hIoPort = NULL;
  268. _hJob = NULL;
  269. // We need to hold onto _piProc.hProcess so the system doesn't reuse the
  270. // process ID.
  271. if (fFireEvent)
  272. {
  273. if (fTerminated)
  274. {
  275. PostToThread(_pSH, MD_PROCESSTERMINATED, &pThis, sizeof(CProcessThread*));
  276. }
  277. else
  278. {
  279. PostToThread(_pSH, MD_PROCESSEXITED, &pThis, sizeof(CProcessThread*));
  280. }
  281. }
  282. GetSystemTimeAsFileTime((FILETIME*)&_i64ExitTime);
  283. TraceTag((tagProcess, "Exiting process thread!"));
  284. Release();
  285. ExitThread(0);
  286. }
  287. //+---------------------------------------------------------------------------
  288. //
  289. // Member: CProcessThread::RealThreadRoutine, public
  290. //
  291. // Synopsis: Main loop which runs this thread while it is active. The thread
  292. // will terminate if this method returns. Termination normally
  293. // happens when the client disconnects by calling Terminate().
  294. //
  295. //----------------------------------------------------------------------------
  296. DWORD
  297. CProcessThread::ThreadMain()
  298. {
  299. DWORD dwRet;
  300. HANDLE ahEvents[3];
  301. int cEvents = 2;
  302. HRESULT hr;
  303. PROCESS_PARAMS *pParams = (PROCESS_PARAMS *)_pvParams;
  304. _ProcParams.Copy(pParams);
  305. #if DBG == 1
  306. {
  307. char achBuf[10];
  308. CStr cstrCmd;
  309. cstrCmd.Set(pParams->pszCommand);
  310. cstrCmd.GetMultiByte(achBuf, 10);
  311. SetName(achBuf);
  312. }
  313. #endif
  314. AddRef();
  315. hr = LaunchProcess(pParams);
  316. ThreadStarted(hr); // Release our calling thread
  317. if (hr)
  318. {
  319. Terminate();
  320. return 1; // Just in case
  321. }
  322. ahEvents[0] = _hCommEvent;
  323. ahEvents[1] = _piProc.hProcess;
  324. while (TRUE)
  325. {
  326. //
  327. // Anonymous pipes don't support asynchronous I/O, and we can't wait
  328. // on a handle to see if there's data on the completion port. So,
  329. // what we do is poll for those every 2 seconds instead.
  330. //
  331. dwRet = WaitForMultipleObjects(cEvents,
  332. ahEvents,
  333. FALSE,
  334. (_hPipe || _hIoPort)
  335. ? PIPEREAD_TIMEOUT * 1000
  336. : INFINITE);
  337. if (dwRet == WAIT_OBJECT_0)
  338. {
  339. //
  340. // Another thread is sending us a message.
  341. //
  342. HandleThreadMessage();
  343. }
  344. else if (dwRet == WAIT_OBJECT_0 + 1)
  345. {
  346. //
  347. // The process terminated.
  348. //
  349. HandleProcessExit();
  350. }
  351. else if (dwRet == WAIT_TIMEOUT)
  352. {
  353. ReadPipeData();
  354. CheckIoPort();
  355. }
  356. else if (dwRet == WAIT_FAILED)
  357. {
  358. AssertSz(FALSE, "NONFATAL: WaitForMultipleObjectsFailure");
  359. break;
  360. }
  361. }
  362. // Make sure we clean everything up OK
  363. Terminate();
  364. return 0;
  365. }
  366. //+---------------------------------------------------------------------------
  367. //
  368. // Member: CProcessThread::HandleThreadMessage, public
  369. //
  370. // Synopsis: Handles any messages other threads send us.
  371. //
  372. //----------------------------------------------------------------------------
  373. void
  374. CProcessThread::HandleThreadMessage()
  375. {
  376. THREADMSG tm;
  377. BYTE bData[MSGDATABUFSIZE];
  378. DWORD cbData;
  379. while (GetNextMsg(&tm, (void **)bData, &cbData))
  380. {
  381. switch (tm)
  382. {
  383. case MD_PLEASEEXIT:
  384. //
  385. // We're being asked to terminate.
  386. //
  387. Terminate();
  388. break;
  389. default:
  390. AssertSz(FALSE, "FATAL: CProcessThread got a message it couldn't handle!");
  391. break;
  392. }
  393. }
  394. }
  395. //+---------------------------------------------------------------------------
  396. //
  397. // Member: CProcessThread::HandleProcessExit, public
  398. //
  399. // Synopsis: The process we started has exited. Send out the status and
  400. // exit.
  401. //
  402. //----------------------------------------------------------------------------
  403. void
  404. CProcessThread::HandleProcessExit()
  405. {
  406. //
  407. // Flush out any data the process may have written to the pipe.
  408. //
  409. ReadPipeData();
  410. // Save the exit code
  411. if (!_fUseExitCode)
  412. {
  413. GetExitCodeProcess(_piProc.hProcess, &_dwExitCode);
  414. _fUseExitCode = TRUE;
  415. AssertSz(_dwExitCode != STILL_ACTIVE, "LEAK: Exited process still active!");
  416. }
  417. Terminate();
  418. }
  419. //+---------------------------------------------------------------------------
  420. //
  421. // Member: CProcessThread::IsDataInPipe, public
  422. //
  423. // Synopsis: Is there any data waiting to be read from the anonymous pipe?
  424. //
  425. //----------------------------------------------------------------------------
  426. BOOL
  427. CProcessThread::IsDataInPipe()
  428. {
  429. DWORD dwBytesAvail = 0;
  430. BOOL fSuccess;
  431. if (!_hPipe)
  432. return FALSE;
  433. fSuccess = PeekNamedPipe(_hPipe,
  434. NULL,
  435. NULL,
  436. NULL,
  437. &dwBytesAvail,
  438. NULL);
  439. if (!fSuccess)
  440. {
  441. //
  442. // Stop trying to read from the pipe
  443. //
  444. CloseHandle(_hPipe);
  445. _hPipe = NULL;
  446. }
  447. return (dwBytesAvail != 0);
  448. }
  449. //+---------------------------------------------------------------------------
  450. //
  451. // Member: CProcessThread::ReadPipeData, public
  452. //
  453. // Synopsis: Read any and all data which is waiting on the pipe. This will
  454. // be anything that the process writes to STDOUT.
  455. //
  456. //----------------------------------------------------------------------------
  457. void
  458. CProcessThread::ReadPipeData()
  459. {
  460. BOOL fSuccess = TRUE;
  461. DWORD cbRead;
  462. char *pch;
  463. while (IsDataInPipe())
  464. {
  465. CStr cstrTemp;
  466. fSuccess = ReadFile(_hPipe,
  467. _abBuffer,
  468. PIPE_BUFFER_SIZE - 1,
  469. &cbRead,
  470. NULL);
  471. if (!fSuccess || cbRead == 0)
  472. {
  473. Terminate();
  474. return; // just in case
  475. }
  476. _abBuffer[cbRead] = '\0';
  477. //
  478. // Is the process letting us know that we should pretend the exit code
  479. // is some value other than the actual exit code?
  480. // (using PROCESS_EXIT_CODE=x in its STDOUT stream)
  481. //
  482. pch = strstr((const char*)_abBuffer, g_pszExitCodeStr);
  483. if (pch)
  484. {
  485. _dwExitCode = atol(pch + strlen(g_pszExitCodeStr));
  486. _fUseExitCode = TRUE;
  487. }
  488. // The string is now in _abBuffer. Add it to our buffer.
  489. //
  490. // Assume the data coming across is MultiByte string data.
  491. //
  492. LOCK_LOCALS(this);
  493. _cstrOutput.AppendMultiByte((const char *)_abBuffer);
  494. }
  495. }
  496. //+---------------------------------------------------------------------------
  497. //
  498. // Member: CProcessThread::CheckIoPort, public
  499. //
  500. // Synopsis: Check the CompletionPort that we are using to receive messages
  501. // from the job object. This will tell us if any process we
  502. // created or that is a child of the one we created crashed.
  503. //
  504. //----------------------------------------------------------------------------
  505. void
  506. CProcessThread::CheckIoPort()
  507. {
  508. DWORD dwCode = 0;
  509. DWORD dwKey = 0;
  510. DWORD dwParam = 0;;
  511. if (!_hIoPort)
  512. {
  513. return;
  514. }
  515. while (GetQueuedCompletionStatus(_hIoPort,
  516. &dwCode,
  517. &dwKey,
  518. (LPOVERLAPPED*)&dwParam,
  519. 0))
  520. {
  521. AssertSz(dwKey == (DWORD)this, "NONFATAL: Bogus port value from GetQueuedCompletionStatus");
  522. switch (dwCode)
  523. {
  524. case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:
  525. {
  526. CProcessThread *pThis = this;
  527. // There is no reliable way to get the actual exit code, since
  528. // the process is already gone and we don't have a handle to it.
  529. TraceTag((tagProcess, "Process %d crashed!", dwParam));
  530. PostToThread(_pSH, MD_PROCESSCRASHED, &pThis, sizeof(CProcessThread*));
  531. }
  532. break;
  533. case JOB_OBJECT_MSG_EXIT_PROCESS:
  534. TraceTag((tagProcess, "Process %d exited.", dwParam));
  535. break;
  536. case JOB_OBJECT_MSG_NEW_PROCESS:
  537. TraceTag((tagProcess, "Process %d started.", dwParam));
  538. // Remember the process id so we can use it later
  539. _aryProcIds.Append(dwParam);
  540. break;
  541. default:
  542. break;
  543. }
  544. }
  545. DWORD dwError = GetLastError();
  546. if (dwError != WAIT_TIMEOUT)
  547. {
  548. TraceTag((tagProcess, "GetQueuedCompletionStatus returned %d", dwError));
  549. }
  550. return;
  551. }
  552. //+---------------------------------------------------------------------------
  553. //
  554. // Member: CProcessThread::LaunchProcess, public
  555. //
  556. // Synopsis: Start the child process for the given command.
  557. //
  558. //----------------------------------------------------------------------------
  559. HRESULT
  560. CProcessThread::LaunchProcess(const PROCESS_PARAMS *pProcParams)
  561. {
  562. BOOL fSuccess;
  563. TCHAR achCommand[MAX_PATH * 2];
  564. TCHAR achDir[MAX_PATH];
  565. BOOL fUseDir;
  566. HRESULT hr = S_OK;
  567. CStr cstrEnvironment;
  568. STARTUPINFO si = { 0 };
  569. HANDLE hPipeStdout = NULL;
  570. HANDLE hPipeStderr = NULL;
  571. if (pProcParams->fGetOutput)
  572. {
  573. //
  574. // Setup the anonymous pipe which we will use to get status from the
  575. // process. Anytime it writes to STDOUT or STDERR it will come across
  576. // this pipe to us.
  577. //
  578. SECURITY_ATTRIBUTES sa;
  579. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  580. sa.lpSecurityDescriptor = NULL;
  581. sa.bInheritHandle = TRUE;
  582. fSuccess = CreatePipe(&_hPipe, &hPipeStdout, &sa, 0);
  583. if (!fSuccess)
  584. {
  585. AssertSz(FALSE, "FATAL: Could not create anonymous pipe");
  586. goto Win32Error;
  587. }
  588. //
  589. // Change our end of the pipe to non-inheritable so the new process
  590. // doesn't pick it up.
  591. //
  592. fSuccess = DuplicateHandle(GetCurrentProcess(),
  593. _hPipe,
  594. GetCurrentProcess(),
  595. NULL,
  596. 0,
  597. FALSE,
  598. DUPLICATE_SAME_ACCESS);
  599. if (!fSuccess)
  600. {
  601. AssertSz(FALSE, "FATAL: Error removing inheritance from handle!");
  602. goto Win32Error;
  603. }
  604. //
  605. // Now duplicate the stdout handle for stderr
  606. //
  607. fSuccess = DuplicateHandle(GetCurrentProcess(),
  608. hPipeStdout,
  609. GetCurrentProcess(),
  610. &hPipeStderr,
  611. 0,
  612. TRUE,
  613. DUPLICATE_SAME_ACCESS);
  614. if (!fSuccess)
  615. {
  616. AssertSz(FALSE, "Error duplicating stdout handle!");
  617. goto Win32Error;
  618. }
  619. }
  620. else
  621. {
  622. hPipeStdout = GetStdHandle(STD_OUTPUT_HANDLE);
  623. hPipeStderr = GetStdHandle(STD_ERROR_HANDLE);
  624. }
  625. if (_hJob)
  626. {
  627. JOBOBJECT_EXTENDED_LIMIT_INFORMATION joLimit = { 0 };
  628. JOBOBJECT_ASSOCIATE_COMPLETION_PORT joPort = { 0 };
  629. if (pProcParams->fNoCrashPopup)
  630. {
  631. // Force crashes to terminate the job (and all processes in it).
  632. joLimit.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
  633. fSuccess = SetInformationJobObject(_hJob,
  634. JobObjectExtendedLimitInformation,
  635. &joLimit,
  636. sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
  637. if (!fSuccess) // Ignore failures except to report them.
  638. {
  639. TraceTag((tagError,
  640. "SetInformationJobObject failed with %d",
  641. GetLastError()));
  642. }
  643. }
  644. // Now we need to setup a completion port so we can find out if one
  645. // of the processes crashed.
  646. _hIoPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (DWORD)this, 0);
  647. if (_hIoPort)
  648. {
  649. joPort.CompletionKey = this;
  650. joPort.CompletionPort = _hIoPort;
  651. fSuccess = SetInformationJobObject(_hJob,
  652. JobObjectAssociateCompletionPortInformation,
  653. &joPort,
  654. sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT));
  655. if (!fSuccess) // Ignore failures except to report them.
  656. {
  657. TraceTag((tagError,
  658. "Failed to set completion port on job: %d",
  659. GetLastError()));
  660. }
  661. }
  662. else
  663. {
  664. TraceTag((tagError,
  665. "CreateIoCompletionPort failed with %d!",
  666. GetLastError()));
  667. }
  668. }
  669. si.cb = sizeof(STARTUPINFO);
  670. if (pProcParams->fMinimize)
  671. {
  672. si.wShowWindow = SW_SHOWMINNOACTIVE;
  673. }
  674. else
  675. {
  676. si.wShowWindow = SW_SHOWNORMAL;
  677. }
  678. if (pProcParams->pszTitle != NULL && _tcslen(pProcParams->pszTitle) > 0)
  679. {
  680. si.lpTitle = pProcParams->pszTitle;
  681. }
  682. //
  683. // Setup our inherited standard handles.
  684. //
  685. si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  686. si.hStdOutput = hPipeStdout;
  687. si.hStdError = hPipeStderr;
  688. si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  689. if (!pProcParams->fGetOutput)
  690. {
  691. si.dwFlags &= ~STARTF_USESTDHANDLES;
  692. }
  693. //
  694. // Expand environment strings in the command name as well as the working
  695. // dir.
  696. //
  697. ExpandEnvironmentStrings(pProcParams->pszCommand,
  698. achCommand,
  699. MAX_PATH * 2);
  700. fUseDir = pProcParams->pszDir && _tcslen(pProcParams->pszDir) > 0;
  701. if (fUseDir)
  702. {
  703. WCHAR *psz;
  704. WCHAR achDir2[MAX_PATH];
  705. ExpandEnvironmentStrings(pProcParams->pszDir, achDir2, MAX_PATH);
  706. GetFullPathName(achDir2, MAX_PATH, achDir, &psz);
  707. }
  708. GetProcessEnvironment(&cstrEnvironment, pProcParams->fNoEnviron);
  709. TraceTag((tagProcess, "Launching Process: %ls in %ls", achCommand, achDir));
  710. //
  711. // Let's do it!
  712. //
  713. fSuccess = CreateProcess(NULL,
  714. achCommand,
  715. NULL,
  716. NULL,
  717. TRUE,
  718. BELOW_NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
  719. cstrEnvironment,
  720. (fUseDir) ? achDir : NULL,
  721. &si,
  722. &_piProc);
  723. if (!fSuccess)
  724. goto Win32Error;
  725. if (_hJob)
  726. {
  727. if (!AssignProcessToJobObject(_hJob, _piProc.hProcess))
  728. {
  729. TraceTag((tagError, "AssignProcessToJobObject failed with %d", GetLastError()));
  730. CloseHandle(_hJob);
  731. _hJob = NULL;
  732. }
  733. }
  734. TraceTag((tagProcess, "Created process PID=%d %ls", _piProc.dwProcessId, achCommand));
  735. CloseHandle(_piProc.hThread);
  736. Cleanup:
  737. if (pProcParams->fGetOutput)
  738. {
  739. //
  740. // The write handles have been inherited by the child process, so we
  741. // let go of them.
  742. //
  743. CloseHandle(hPipeStdout);
  744. CloseHandle(hPipeStderr);
  745. }
  746. return hr;
  747. Win32Error:
  748. hr = HRESULT_FROM_WIN32(GetLastError());
  749. TraceTag((tagProcess, "Error creating process: %x", hr));
  750. goto Cleanup;
  751. }
  752. //+---------------------------------------------------------------------------
  753. //
  754. // Member: CProcessThread::GetProcessEnvironment, public
  755. //
  756. // Synopsis: Builds an environment block for the new process that has
  757. // our custom id so we can identify them.
  758. //
  759. // Arguments: [pcstr] -- Place to put environment block
  760. // [fNoEnviron] -- If TRUE, don't inherit the environment block
  761. //
  762. //----------------------------------------------------------------------------
  763. void
  764. CProcessThread::GetProcessEnvironment(CStr *pcstr, BOOL fNoEnviron)
  765. {
  766. TCHAR *pszEnviron;
  767. TCHAR achNewVar[50];
  768. int cch = 1;
  769. TCHAR *pch;
  770. static long s_lProcID = 0;
  771. _lEnvID = InterlockedIncrement(&s_lProcID);
  772. wsprintf(achNewVar, _T("__MTSCRIPT_ENV_ID=%d\0"), _lEnvID);
  773. if (!fNoEnviron)
  774. {
  775. pszEnviron = GetEnvironmentStrings();
  776. pch = pszEnviron;
  777. cch = 2; // Always have two terminating nulls at least
  778. while (*pch || *(pch+1))
  779. {
  780. pch++;
  781. cch++;
  782. }
  783. pcstr->Set(NULL, cch + _tcslen(achNewVar));
  784. memcpy((LPTSTR)*pcstr, pszEnviron, cch * sizeof(TCHAR));
  785. FreeEnvironmentStrings(pszEnviron);
  786. }
  787. else
  788. {
  789. pcstr->Set(NULL, _tcslen(achNewVar) + 1);
  790. }
  791. memcpy((LPTSTR)*pcstr + cch - 1,
  792. achNewVar,
  793. (_tcslen(achNewVar)+1) * sizeof(TCHAR));
  794. }