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.

762 lines
19 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Scheduling Agent Service
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  7. //
  8. // File: sageset.cxx
  9. //
  10. // Contents: Support for Sage Settings
  11. //
  12. // History: 18-Jul-96 EricB created
  13. // 3-06-1997 DavidMun added code to create sagerun argument
  14. // 4-16-1997 DavidMun made it use strings from UI instead of
  15. // from job object.
  16. //
  17. //-----------------------------------------------------------------------------
  18. #include "..\pch\headers.hxx"
  19. #pragma hdrstop
  20. #include <mstask.h>
  21. #include <job_cls.hxx>
  22. #include <debug.hxx>
  23. #include <sage.hxx>
  24. #include "sageset.hxx"
  25. #include "..\inc\misc.hxx"
  26. #include "..\folderui\macros.h" // get ARRAYLEN macro
  27. const CHAR SAGE_KEY[] = "SOFTWARE\\Microsoft\\Plus!\\System Agent\\SAGE";
  28. const CHAR PROGRAM_VALUE[] ="Program";
  29. const CHAR SETTINGS_VALUE[] = "Settings";
  30. const CHAR SAGESET_PARAM[] = " /SAGESET:";
  31. BOOL GetAppNameFromPath(CHAR * pszFullPathName, CHAR * pszAppName);
  32. BOOL AppNamesMatch(CHAR * pszCommand1, CHAR * pszCommand2);
  33. BOOL fAppCantSupportMultipleInstances(CHAR * pszCmd);
  34. BOOL AnsiToIntA(LPCSTR pszString, int * piRet);
  35. HRESULT GetNextSageRunParam(HKEY hkSageApp, PINT pnSetNum);
  36. //+----------------------------------------------------------------------------
  37. //
  38. // Function: DoSageSettings
  39. //
  40. // Synopsis: Invoke the Sage-aware app to alter its schedule settings.
  41. //
  42. // Arguments: [szCommand] - app name portion of command line
  43. // [szSageRun] - "" or contains /SAGERUN switch on input.
  44. // contains /SAGERUN switch on output if app is
  45. // sage-aware.
  46. //
  47. // Modifies: *[szSageRun]
  48. //
  49. // Returns: S_OK - application launched
  50. // S_FALSE - application doesn't support sage settings
  51. // E_*
  52. //
  53. // History: 3-06-1997 DavidMun Added [szNewArg] & [cchNewArg].
  54. // 4-16-1997 DavidMun Take strings from UI instead of job
  55. // object.
  56. //
  57. //-----------------------------------------------------------------------------
  58. HRESULT
  59. DoSageSettings(
  60. LPSTR szCommand,
  61. LPSTR szSageRun)
  62. {
  63. int nSetNum = 0;
  64. if (!IsSageAware(szCommand, szSageRun, &nSetNum))
  65. {
  66. return S_FALSE;
  67. }
  68. //
  69. // We are here because the user hit the "Settings..." button, so if the
  70. // arguments don't include a sageset parameter, we need to add it.
  71. //
  72. if (!*szSageRun)
  73. {
  74. //
  75. // IsSageAware set nSetNum to the next valid number to use.
  76. //
  77. wsprintf(szSageRun, "%s%u", SAGERUN_PARAM, nSetNum);
  78. }
  79. //
  80. // Create a command line to invoke the sage-aware app with the sageset
  81. // parameter.
  82. //
  83. CHAR szSetCommand[MAX_PATH];
  84. CHAR szSetParams[MAX_PATH];
  85. lstrcpyn(szSetCommand, szCommand, MAX_PATH);
  86. lstrcpy(szSetParams, SAGESET_PARAM);
  87. wsprintf(&szSetParams[lstrlen(szSetParams)], "%u", nSetNum);
  88. if (!GetAppNameFromPath(szSetCommand, NULL)) //got a path?
  89. {
  90. //
  91. // GetAppNameFromPath returns FALSE if szSetCommand is not a full
  92. // path. So, see if the app has registered a path. If it has,
  93. // GetAppPath places the full path name in szFullyQualified.
  94. //
  95. CHAR szFullyQualified[MAX_PATH];
  96. GetAppPathInfo(szSetCommand,
  97. szFullyQualified,
  98. MAX_PATH,
  99. NULL,
  100. 0);
  101. if (*szFullyQualified)
  102. {
  103. lstrcpy(szSetCommand, szFullyQualified);
  104. }
  105. }
  106. //
  107. // Prefix the system path with the directories listed by the application
  108. // in the app paths key.
  109. //
  110. BOOL fChangedPath;
  111. LPSTR pszSavedPath;
  112. fChangedPath = SetAppPath(szSetCommand, &pszSavedPath);
  113. //
  114. // Put the SageSet param onto the command line.
  115. //
  116. if (szSetParams[0] != ' ')
  117. {
  118. lstrcat(szSetCommand, " ");
  119. }
  120. lstrcat(szSetCommand, szSetParams);
  121. DWORD dwErr = ERROR_SUCCESS;
  122. STARTUPINFO sui;
  123. PROCESS_INFORMATION pi;
  124. ZeroMemory(&sui, sizeof(sui));
  125. sui.cb = sizeof (STARTUPINFO);
  126. if (CreateProcess(NULL,
  127. szSetCommand,
  128. NULL,
  129. NULL,
  130. FALSE,
  131. CREATE_NEW_CONSOLE |
  132. CREATE_NEW_PROCESS_GROUP,
  133. NULL,
  134. NULL,
  135. &sui,
  136. &pi))
  137. {
  138. CloseHandle(pi.hThread);
  139. CloseHandle(pi.hProcess);
  140. }
  141. else
  142. {
  143. dwErr = GetLastError();
  144. ERR_OUT("DoSageSettings: CreateProcess", dwErr);
  145. }
  146. if (fChangedPath)
  147. {
  148. SetEnvironmentVariable(TEXT("PATH"), pszSavedPath);
  149. delete [] pszSavedPath;
  150. }
  151. return (dwErr == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(dwErr);
  152. }
  153. //+--------------------------------------------------------------------------
  154. //
  155. // Function: IsSageAwareW
  156. //
  157. // Synopsis: Unicode wrapper for IsSageAware
  158. //
  159. // History: 10-27-1997 DavidMun Created
  160. //
  161. //---------------------------------------------------------------------------
  162. BOOL
  163. IsSageAwareW(WCHAR *pwzCmd, WCHAR *pwzParams, int *pnSetNum)
  164. {
  165. HRESULT hr;
  166. CHAR szCmd[MAX_PATH+1];
  167. CHAR szParam[MAX_PATH+1];
  168. BOOL fResult = FALSE;
  169. do
  170. {
  171. hr = UnicodeToAnsi(szCmd, pwzCmd, ARRAYLEN(szCmd));
  172. BREAK_ON_FAIL(hr);
  173. hr = UnicodeToAnsi(szParam, pwzParams, ARRAYLEN(szParam));
  174. BREAK_ON_FAIL(hr);
  175. fResult = IsSageAware(szCmd, szParam, pnSetNum);
  176. } while (0);
  177. return fResult;
  178. }
  179. //+----------------------------------------------------------------------------
  180. //
  181. // Function: IsSageAware
  182. //
  183. // Synopsis: Does this app use Sage settings?
  184. //
  185. // Arguments: [pszCmd] - The task application name property.
  186. // [pszParams] - The task parameters, can be NULL if pnSetNum is
  187. // NULL.
  188. // [pnSetNum] - the registry setting set number to return, can
  189. // be NULL if not needed.
  190. //
  191. // Returns: TRUE if it does, FALSE otherwise.
  192. //
  193. //-----------------------------------------------------------------------------
  194. BOOL
  195. IsSageAware(CHAR * pszCmd, const CHAR * pszParams, int * pnSetNum)
  196. {
  197. int index;
  198. HKEY hSageKey;
  199. HKEY hSageSubKey;
  200. CHAR szSubKey[MAXPATH];
  201. CHAR szSubKeyPath[MAXPATH];
  202. CHAR szRegValue[MAXCOMMANDLINE];
  203. long lRet;
  204. CHAR *p;
  205. BOOL fResult = FALSE;
  206. DWORD cb;
  207. if (pnSetNum != NULL)
  208. {
  209. *pnSetNum = 0;
  210. }
  211. if (RegOpenKey(HKEY_LOCAL_MACHINE, SAGE_KEY, &hSageKey))
  212. {
  213. return FALSE;
  214. }
  215. #define MAXSAGEPROGS 0xffffff
  216. for (index = 0; index < MAXSAGEPROGS; index++)
  217. {
  218. //
  219. // Examine each subkey of the Sage key.
  220. //
  221. lRet = RegEnumKey(hSageKey, index, szSubKey, MAXPATH);
  222. if (lRet != ERROR_SUCCESS)
  223. {
  224. break;
  225. }
  226. lstrcpy(szSubKeyPath, SAGE_KEY);
  227. lstrcat(szSubKeyPath, "\\");
  228. lstrcat(szSubKeyPath, szSubKey);
  229. if (RegOpenKey(HKEY_LOCAL_MACHINE,
  230. szSubKeyPath,
  231. &hSageSubKey) != ERROR_SUCCESS)
  232. {
  233. continue; //no path
  234. }
  235. //
  236. // Look for a match on the application name.
  237. //
  238. cb = MAXPATH;
  239. if (RegQueryValueEx(hSageSubKey,
  240. PROGRAM_VALUE,
  241. NULL,
  242. NULL,
  243. (LPBYTE)szRegValue,
  244. &cb) != ERROR_SUCCESS)
  245. {
  246. RegCloseKey(hSageSubKey);
  247. continue; //no path
  248. }
  249. schDebugOut((DEB_ITRACE,
  250. "IsSageAware enum: pszCmd = %s, szRegValue = %s\n",
  251. pszCmd, szRegValue));
  252. RegCloseKey(hSageSubKey);
  253. if (AppNamesMatch(pszCmd, szRegValue))
  254. {
  255. if (RegOpenKey(HKEY_LOCAL_MACHINE,
  256. szSubKeyPath,
  257. &hSageSubKey) != ERROR_SUCCESS)
  258. {
  259. continue; //no settings dialog
  260. }
  261. fResult = FALSE;
  262. if (RegQueryValueEx(hSageSubKey,
  263. SETTINGS_VALUE,
  264. NULL,
  265. NULL,
  266. (LPBYTE)szRegValue,
  267. &cb) == ERROR_SUCCESS)
  268. {
  269. if (szRegValue[0] == 0)
  270. {
  271. RegCloseKey(hSageSubKey);
  272. break; // this means don't allow sage settings
  273. }
  274. }
  275. else
  276. {
  277. RegCloseKey(hSageSubKey);
  278. continue; //no settings registry key
  279. }
  280. // test for app that can't handle multiple instances
  281. //
  282. if (!fAppCantSupportMultipleInstances(pszCmd))
  283. {
  284. fResult = TRUE;
  285. }
  286. if (fResult && pnSetNum != NULL)
  287. {
  288. //
  289. // Extract the set number from the parameters, if requested.
  290. //
  291. p = _tcsstr(pszParams, SAGERUN_PARAM);
  292. if (p != NULL)
  293. {
  294. AnsiToIntA(p + lstrlen(SAGERUN_PARAM), pnSetNum);
  295. }
  296. else
  297. {
  298. //
  299. // This job is for a sage-aware app which supports the
  300. // SAGERUN switch, but it lacks that switch in its
  301. // parameter property. Since the caller wants to know
  302. // what value to use, generate one.
  303. //
  304. HRESULT hr = GetNextSageRunParam(hSageSubKey, pnSetNum);
  305. if (FAILED(hr))
  306. {
  307. fResult = FALSE;
  308. }
  309. }
  310. }
  311. RegCloseKey(hSageSubKey);
  312. break;
  313. }
  314. }
  315. RegCloseKey(hSageKey);
  316. return fResult;
  317. }
  318. const CHAR SET_PREFIX[] = "Set";
  319. //+--------------------------------------------------------------------------
  320. //
  321. // Function: CreateSageRunKey
  322. //
  323. // Synopsis: Create a registry key with string representation of
  324. // value [uiKey] for app [szSageAwareExe].
  325. //
  326. // Arguments: [szSageAwareExe] - app for which to create key
  327. // [uiKey] - numeric representation of key name
  328. //
  329. // Returns: S_OK or E_FAIL
  330. //
  331. // History: 10-28-1997 DavidMun Created
  332. //
  333. //---------------------------------------------------------------------------
  334. HRESULT
  335. CreateSageRunKey(
  336. LPCSTR szSageAwareExe,
  337. UINT uiKey)
  338. {
  339. HRESULT hr = E_FAIL; // init for failure
  340. int index;
  341. HKEY hSageKey;
  342. HKEY hSageSubKey;
  343. CHAR szSubKey[MAXPATH];
  344. CHAR szSubKeyPath[MAXPATH];
  345. CHAR szRegValue[MAXCOMMANDLINE];
  346. long lRet;
  347. DWORD cb;
  348. if (RegOpenKey(HKEY_LOCAL_MACHINE, SAGE_KEY, &hSageKey))
  349. {
  350. return hr;
  351. }
  352. for (index = 0; index < MAXSAGEPROGS; index++)
  353. {
  354. //
  355. // Examine each subkey of the Sage key.
  356. //
  357. lRet = RegEnumKey(hSageKey, index, szSubKey, MAXPATH);
  358. if (lRet != ERROR_SUCCESS)
  359. {
  360. break;
  361. }
  362. lstrcpy(szSubKeyPath, SAGE_KEY);
  363. lstrcat(szSubKeyPath, "\\");
  364. lstrcat(szSubKeyPath, szSubKey);
  365. if (RegOpenKey(HKEY_LOCAL_MACHINE,
  366. szSubKeyPath,
  367. &hSageSubKey) != ERROR_SUCCESS)
  368. {
  369. continue; //no path
  370. }
  371. //
  372. // Look for a match on the application name.
  373. //
  374. cb = MAXPATH;
  375. if (RegQueryValueEx(hSageSubKey,
  376. PROGRAM_VALUE,
  377. NULL,
  378. NULL,
  379. (LPBYTE)szRegValue,
  380. &cb) != ERROR_SUCCESS)
  381. {
  382. RegCloseKey(hSageSubKey);
  383. continue; //no path
  384. }
  385. RegCloseKey(hSageSubKey);
  386. if (AppNamesMatch((LPSTR)szSageAwareExe, szRegValue))
  387. {
  388. if (RegOpenKey(HKEY_LOCAL_MACHINE,
  389. szSubKeyPath,
  390. &hSageSubKey) != ERROR_SUCCESS)
  391. {
  392. continue; //no settings dialog
  393. }
  394. CHAR szKeyName[MAX_PATH];
  395. HKEY hkNewKey = NULL;
  396. wsprintf(szKeyName, "%s%u", SET_PREFIX, uiKey);
  397. lRet = RegCreateKey(hSageSubKey, szKeyName, &hkNewKey);
  398. if (hkNewKey)
  399. {
  400. RegCloseKey(hkNewKey);
  401. }
  402. RegCloseKey(hSageSubKey);
  403. if (lRet == ERROR_SUCCESS)
  404. {
  405. hr = S_OK;
  406. }
  407. else
  408. {
  409. schDebugOut((DEB_ERROR,
  410. "CreateSageRunKey: RegCreateKey %uL",
  411. lRet));
  412. }
  413. break;
  414. }
  415. }
  416. RegCloseKey(hSageKey);
  417. return hr;
  418. }
  419. //+--------------------------------------------------------------------------
  420. //
  421. // Function: GetNextSageRunParam
  422. //
  423. // Synopsis: Fill *[pnSetNum] with the next <n> value to use for a new
  424. // Set<n> subkey under [hSageAppSubKey].
  425. //
  426. // Arguments: [hSageAppSubKey] - handle to app's key under SAGE key
  427. // [pnSetNum] - filled with next number to use
  428. //
  429. // Returns: HRESULT
  430. //
  431. // Modifies: *[pnSetNum]
  432. //
  433. // History: 3-06-1997 DavidMun Created
  434. //
  435. //---------------------------------------------------------------------------
  436. HRESULT
  437. GetNextSageRunParam(
  438. HKEY hSageAppSubKey,
  439. PINT pnSetNum)
  440. {
  441. HRESULT hr = S_OK;
  442. ULONG idxKey;
  443. *pnSetNum = 0;
  444. for (idxKey = 0; TRUE; idxKey++)
  445. {
  446. LONG lr;
  447. CHAR szSubKey[MAX_PATH + 1];
  448. lr = RegEnumKey(hSageAppSubKey, idxKey, szSubKey, ARRAYLEN(szSubKey));
  449. //
  450. // Quit on error, including end of subkeys
  451. //
  452. if (lr != ERROR_SUCCESS)
  453. {
  454. if (lr != ERROR_NO_MORE_ITEMS)
  455. {
  456. hr = E_FAIL;
  457. schDebugOut((DEB_ERROR,
  458. "GetNextSageRunParam: RegEnumKey %uL",
  459. lr));
  460. }
  461. break;
  462. }
  463. //
  464. // Ignore this key if it doesn't start with "Set"
  465. //
  466. CHAR szSet[ARRAYLEN(SET_PREFIX)];
  467. lstrcpyn(szSet, szSubKey, ARRAYLEN(SET_PREFIX));
  468. if (lstrcmpi(szSet, SET_PREFIX))
  469. {
  470. continue;
  471. }
  472. //
  473. // Get the numeric value after the prefix. If there's no valid
  474. // number, ignore this key.
  475. //
  476. BOOL fNumber;
  477. INT iSetN;
  478. fNumber = AnsiToIntA(szSubKey + ARRAYLEN(SET_PREFIX) - 1, &iSetN);
  479. if (!fNumber)
  480. {
  481. continue;
  482. }
  483. //
  484. // If the number matches or exceeds the one we plan to use, make ours
  485. // larger.
  486. //
  487. if (iSetN >= *pnSetNum)
  488. {
  489. *pnSetNum = iSetN + 1;
  490. }
  491. }
  492. return hr;
  493. }
  494. //+---------------------------------------------------------------------------
  495. //
  496. // Function: GetAppNameFromPath
  497. //
  498. // Synopsis:
  499. //
  500. // Arguments: [pszFullPathName] - full or partial path
  501. // [pszAppName] - buffer for app name, may be NULL
  502. //
  503. // Returns: TRUE - [pszFullPathName] contained slashes
  504. // FALSE - [pszFullPathName] didn't contain slashes
  505. //
  506. // Modifies: *[pszAppName]
  507. //
  508. // History: 10-25-96 DavidMun Made DBCS safe
  509. //
  510. //----------------------------------------------------------------------------
  511. BOOL GetAppNameFromPath(CHAR * pszFullPathName, CHAR * pszAppName)
  512. {
  513. LPSTR pszLastSlash;
  514. pszLastSlash = _tcsrchr(pszFullPathName, '\\');
  515. if (!pszAppName)
  516. {
  517. return pszLastSlash != NULL;
  518. }
  519. if (pszLastSlash)
  520. {
  521. lstrcpy(pszAppName, pszLastSlash + 1);
  522. }
  523. else
  524. {
  525. lstrcpy(pszAppName, pszFullPathName);
  526. }
  527. schDebugOut((DEB_ITRACE, "GetAppNameFromPath app name: %s\n", pszAppName));
  528. if (pszAppName[0] != '"')
  529. {
  530. LPSTR pszQuote = _tcschr(pszAppName, '"');
  531. if (pszQuote)
  532. {
  533. *pszQuote = '\0';
  534. }
  535. }
  536. return pszLastSlash != NULL;
  537. }
  538. BOOL AppNamesMatch(CHAR *pszCommand1, CHAR *pszCommand2)
  539. {
  540. CHAR short1[MAXPATH];
  541. CHAR short2[MAXPATH];
  542. GetAppNameFromPath(pszCommand1, short1);
  543. GetAppNameFromPath(pszCommand2, short2);
  544. if (lstrcmpi(short1, short2) == 0)
  545. {
  546. return(TRUE);
  547. }
  548. return(FALSE);
  549. }
  550. BOOL fAppCantSupportMultipleInstances(CHAR * pszCmd)
  551. {
  552. if (AppNamesMatch(pszCmd, "SCANDSKW.EXE") ||
  553. AppNamesMatch(pszCmd, "DEFRAG.EXE") ||
  554. AppNamesMatch(pszCmd, "CMPAGENT.EXE"))
  555. {
  556. if (FindWindow("ScanDskWDlgClass", NULL))
  557. return TRUE;
  558. if (FindWindow("MSDefragWClass1", NULL))
  559. return TRUE;
  560. if (FindWindow("MSExtraPakWClass1", NULL))
  561. return TRUE;
  562. }
  563. return FALSE;
  564. }
  565. /*----------------------------------------------------------
  566. Purpose: ScottH's version of atoi. Supports hexadecimal too.
  567. If this function returns FALSE, *piRet is set to 0.
  568. Returns: TRUE if the string is a number, or contains a partial number
  569. FALSE if the string is not a number
  570. */
  571. BOOL AnsiToIntA(LPCSTR pszString, int * piRet)
  572. {
  573. #define InRange(id, idFirst, idLast) \
  574. ((UINT)(id-idFirst) <= (UINT)(idLast-idFirst))
  575. #define IS_DIGIT(ch) InRange(ch, '0', '9')
  576. BOOL bRet;
  577. int n;
  578. BOOL bNeg = FALSE;
  579. LPCSTR psz;
  580. LPCSTR pszAdj;
  581. // Skip leading whitespace
  582. //
  583. for (psz = pszString;
  584. *psz == ' ' || *psz == '\n' || *psz == '\t';
  585. psz = NextChar(psz))
  586. ;
  587. // Determine possible explicit signage
  588. //
  589. if (*psz == '+' || *psz == '-')
  590. {
  591. bNeg = (*psz == '+') ? FALSE : TRUE;
  592. psz++;
  593. }
  594. // Or is this hexadecimal?
  595. //
  596. pszAdj = NextChar(psz);
  597. if (*psz == '0' && (*pszAdj == 'x' || *pszAdj == 'X'))
  598. {
  599. // Yes
  600. // (Never allow negative sign with hexadecimal numbers)
  601. bNeg = FALSE;
  602. psz = NextChar(pszAdj);
  603. pszAdj = psz;
  604. // Do the conversion
  605. //
  606. for (n = 0; ; psz = NextChar(psz))
  607. {
  608. if (IS_DIGIT(*psz))
  609. n = 0x10 * n + *psz - '0';
  610. else
  611. {
  612. CHAR ch = *psz;
  613. int n2;
  614. if (ch >= 'a')
  615. ch -= 'a' - 'A';
  616. n2 = ch - 'A' + 0xA;
  617. if (n2 >= 0xA && n2 <= 0xF)
  618. n = 0x10 * n + n2;
  619. else
  620. break;
  621. }
  622. }
  623. // Return TRUE if there was at least one digit
  624. bRet = (psz != pszAdj);
  625. }
  626. else
  627. {
  628. // No
  629. pszAdj = psz;
  630. // Do the conversion
  631. for (n = 0; IS_DIGIT(*psz); psz = NextChar(psz))
  632. n = 10 * n + *psz - '0';
  633. // Return TRUE if there was at least one digit
  634. bRet = (psz != pszAdj);
  635. }
  636. *piRet = bNeg ? -n : n;
  637. return bRet;
  638. }