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.

723 lines
23 KiB

  1. // Contains code that needs to be dual compiled, once for ansi and once for unicode
  2. #include "priv.h"
  3. #include <memt.h>
  4. #include "userenv.h"
  5. BOOL _PathAppend(LPCTSTR pszBase, LPCTSTR pszAppend, LPTSTR pszOut, DWORD cchOut)
  6. {
  7. BOOL bRet = FALSE;
  8. if (SUCCEEDED(StringCchCopy(pszOut, cchOut, pszBase)) &&
  9. SUCCEEDED(StringCchCat(pszOut, cchOut, TEXT("\\"))) &&
  10. SUCCEEDED(StringCchCat(pszOut, cchOut, pszAppend)))
  11. {
  12. bRet = TRUE;
  13. }
  14. return bRet;
  15. }
  16. LWSTDAPI AssocMakeFileExtsToApplication(ASSOCMAKEF flags, LPCTSTR pszExt, LPCTSTR pszApplication)
  17. {
  18. ASSERT(FALSE);
  19. return E_UNEXPECTED;
  20. }
  21. HRESULT _AllocValueString(HKEY hkey, LPCTSTR pszKey, LPCTSTR pszVal, LPTSTR *ppsz)
  22. {
  23. DWORD cb, err;
  24. err = SHGetValue(hkey, pszKey, pszVal, NULL, NULL, &cb);
  25. ASSERT(ppsz);
  26. *ppsz = NULL;
  27. if (NOERROR == err)
  28. {
  29. LPTSTR psz = (LPTSTR) LocalAlloc(LPTR, cb);
  30. if (psz)
  31. {
  32. err = SHGetValue(hkey, pszKey, pszVal, NULL, (LPVOID)psz, &cb);
  33. if (NOERROR == err)
  34. *ppsz = psz;
  35. else
  36. LocalFree(psz);
  37. }
  38. else
  39. err = ERROR_OUTOFMEMORY;
  40. }
  41. return HRESULT_FROM_WIN32(err);
  42. }
  43. // <Swipped from the NT5 version of Shell32>
  44. #define SZ_REGKEY_FILEASSOCIATION TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileAssociation")
  45. LWSTDAPI_(void) PrettifyFileDescription(LPTSTR pszDesc, LPCTSTR pszCutList)
  46. {
  47. LPTSTR pszCutListReg;
  48. if (!pszDesc || !*pszDesc)
  49. return;
  50. // get the Cut list from registry
  51. // this is MULTI_SZ
  52. if (S_OK == _AllocValueString(HKEY_LOCAL_MACHINE, SZ_REGKEY_FILEASSOCIATION, TEXT("CutList"), &pszCutListReg))
  53. {
  54. pszCutList = pszCutListReg;
  55. }
  56. if (pszCutList)
  57. {
  58. // cut strings in cut list from file description
  59. for (LPCTSTR pszCut = pszCutList; *pszCut; pszCut = pszCut + lstrlen(pszCut) + 1)
  60. {
  61. LPTSTR pch = StrRStrI(pszDesc, NULL, pszCut);
  62. // cut the exact substring from the end of file description
  63. if (pch && !*(pch + lstrlen(pszCut)))
  64. {
  65. *pch = '\0';
  66. // remove trailing spaces
  67. for (--pch; (pch >= pszDesc) && (TEXT(' ') == *pch); pch--)
  68. *pch = 0;
  69. break;
  70. }
  71. }
  72. if (pszCutListReg)
  73. LocalFree(pszCutListReg);
  74. }
  75. }
  76. /*
  77. <Swipped from the NT5 version of Shell32>
  78. GetFileDescription retrieves the friendly name from a file's verion rsource.
  79. The first language we try will be the first item in the
  80. "\VarFileInfo\Translations" section; if there's nothing there,
  81. we try the one coded into the IDS_VN_FILEVERSIONKEY resource string.
  82. If we can't even load that, we just use English (040904E4). We
  83. also try English with a null codepage (04090000) since many apps
  84. were stamped according to an old spec which specified this as
  85. the required language instead of 040904E4.
  86. If there is no FileDescription in version resource, return the file name.
  87. Parameters:
  88. LPCTSTR pszPath: full path of the file
  89. LPTSTR pszDesc: pointer to the buffer to receive friendly name. If NULL,
  90. *pcchDesc will be set to the length of friendly name in
  91. characters, including ending NULL, on successful return.
  92. UINT *pcchDesc: length of the buffer in characters. On successful return,
  93. it contains number of characters copied to the buffer,
  94. including ending NULL.
  95. Return:
  96. TRUE on success, and FALSE otherwise
  97. */
  98. BOOL WINAPI SHGetFileDescription(LPCTSTR pszPath, LPCTSTR pszVersionKeyIn, LPCTSTR pszCutListIn, LPTSTR pszDesc, UINT *pcchDesc)
  99. {
  100. UINT cchValue = 0;
  101. TCHAR szPath[MAX_PATH], *pszValue = NULL;
  102. DWORD dwAttribs;
  103. DWORD dwHandle; /* version subsystem handle */
  104. DWORD dwVersionSize; /* size of the version data */
  105. LPTSTR lpVersionBuffer = NULL; /* pointer to version data */
  106. TCHAR szVersionKey[60]; /* big enough for anything we need */
  107. struct _VERXLATE
  108. {
  109. WORD wLanguage;
  110. WORD wCodePage;
  111. } *lpXlate; /* ptr to translations data */
  112. ASSERT(pszPath && *pszPath && pcchDesc);
  113. if (!PathFileExistsAndAttributes(pszPath, &dwAttribs))
  114. {
  115. return FALSE;
  116. }
  117. // copy the path to the dest dir
  118. StrCpyN(szPath, pszPath, ARRAYSIZE(szPath));
  119. if ((dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ||
  120. PathIsUNCServer(pszPath) ||
  121. PathIsUNCServerShare(pszPath))
  122. {
  123. // bail in the \\server, \\server\share, and directory case or else GetFileVersionInfo() will try
  124. // to do a LoadLibraryEx() on the path (which will fail, but not before we seach the entire include
  125. // path which can take a long time)
  126. goto Exit;
  127. }
  128. dwVersionSize = GetFileVersionInfoSize(szPath, &dwHandle);
  129. if (dwVersionSize == 0L)
  130. goto Exit; /* no version info */
  131. lpVersionBuffer = (LPTSTR)LocalAlloc(LPTR, dwVersionSize);
  132. if (lpVersionBuffer == NULL)
  133. goto Exit;
  134. if (!GetFileVersionInfo(szPath, dwHandle, dwVersionSize, lpVersionBuffer))
  135. goto Exit;
  136. // Try same language as the caller
  137. if (pszVersionKeyIn)
  138. {
  139. StrCpyN(szVersionKey, pszVersionKeyIn, ARRAYSIZE(szVersionKey));
  140. if (VerQueryValue(lpVersionBuffer, szVersionKey, (void **)&pszValue, &cchValue))
  141. {
  142. goto Exit;
  143. }
  144. }
  145. // Try first language this supports
  146. // Look for translations
  147. if (VerQueryValue(lpVersionBuffer, TEXT("\\VarFileInfo\\Translation"),
  148. (void **)&lpXlate, &cchValue)
  149. && cchValue)
  150. {
  151. wnsprintf(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\%04X%04X\\FileDescription"),
  152. lpXlate[0].wLanguage, lpXlate[0].wCodePage);
  153. if (VerQueryValue(lpVersionBuffer, szVersionKey, (void **)&pszValue, &cchValue))
  154. goto Exit;
  155. }
  156. #ifdef UNICODE
  157. if (SUCCEEDED(StringCchCopy(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\040904B0\\FileDescription"))) &&
  158. VerQueryValue(lpVersionBuffer, szVersionKey, (void **)&pszValue, &cchValue))
  159. {
  160. goto Exit;
  161. }
  162. #endif
  163. // try English
  164. if (SUCCEEDED(StringCchCopy(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\040904E4\\FileDescription"))) &&
  165. VerQueryValue(lpVersionBuffer, szVersionKey, (void **)&pszValue, &cchValue))
  166. {
  167. goto Exit;
  168. }
  169. // try English, null codepage
  170. if (SUCCEEDED(StringCchCopy(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\04090000\\FileDescription"))) &&
  171. VerQueryValue(lpVersionBuffer, szVersionKey, (void **)&pszValue, &cchValue))
  172. {
  173. goto Exit;
  174. }
  175. Exit:
  176. if (!pszValue || !*pszValue)
  177. {
  178. // Could not find FileVersion info in a reasonable format, return file name
  179. PathRemoveExtension(szPath);
  180. pszValue = PathFindFileName(szPath);
  181. cchValue = lstrlen(pszValue);
  182. }
  183. PrettifyFileDescription(pszValue, pszCutListIn);
  184. cchValue = lstrlen(pszValue) + 1;
  185. if (!pszDesc) // only want to know the length of the friendly name
  186. *pcchDesc = cchValue;
  187. else
  188. {
  189. *pcchDesc = min(*pcchDesc, cchValue);
  190. StrCpyN(pszDesc, pszValue, *pcchDesc);
  191. }
  192. if (lpVersionBuffer)
  193. LocalFree(lpVersionBuffer);
  194. return TRUE;
  195. }
  196. // Convert LPTSTR to LPSTR and return TRUE if the LPSTR can
  197. // be converted back to LPTSTR without unacceptible data loss
  198. //
  199. BOOL DoesStringRoundTrip(LPCTSTR pwszIn, LPSTR pszOut, UINT cchOut)
  200. {
  201. #ifdef UNICODE
  202. // On NT5 we have to be more stringent since you can switch UI
  203. // languages on the fly, thereby breaking this constant codepage
  204. // assumption inherent in the downlevel implementations.
  205. //
  206. // we have to support the function being called with a null pszOut
  207. // just to determine if pwszIn will roundtrip
  208. //
  209. {
  210. LPCTSTR pIn = pwszIn;
  211. LPSTR pOut = pszOut;
  212. UINT cch = cchOut;
  213. while (*pIn)
  214. {
  215. if (*pIn > ((TCHAR)127))
  216. {
  217. if (cchOut) // caller has provided a buffer
  218. {
  219. #ifdef DEBUG
  220. SHUnicodeToAnsiCP(CP_ACPNOVALIDATE, pwszIn, pszOut, cchOut);
  221. #else
  222. SHUnicodeToAnsi(pwszIn, pszOut, cchOut);
  223. #endif
  224. }
  225. return FALSE;
  226. }
  227. if (cch) // we have a buffer and it still has space
  228. {
  229. *pOut++ = (char)*pIn;
  230. if (!--cch)
  231. {
  232. break; // out buffer filled, leave.
  233. }
  234. }
  235. pIn++;
  236. }
  237. // Null terminate the out buffer
  238. if (cch)
  239. {
  240. *pOut = '\0';
  241. }
  242. else if (cchOut)
  243. {
  244. *(pOut-1) = '\0';
  245. }
  246. // Everything was low ascii, no dbcs worries and it will always round-trip
  247. return TRUE;
  248. }
  249. #else
  250. StrCpyN(pszOut, pwszIn, cchOut);
  251. return TRUE;
  252. #endif
  253. }
  254. DWORD _ExpandRegString(PTSTR pszData, DWORD cchData, DWORD *pcchSize)
  255. {
  256. DWORD err = ERROR_OUTOFMEMORY;
  257. PTSTR psz = StrDup(pszData);
  258. if (psz)
  259. {
  260. // now we will try to expand back into the target buffer
  261. // NOTE we deliberately dont use SHExpandEnvironmentStrings
  262. // since it will not give us the size we need
  263. // we have to use
  264. #ifdef UNICODE
  265. *pcchSize = ExpandEnvironmentStringsW(psz, pszData, cchData);
  266. #else
  267. *pcchSize = ExpandEnvironmentStringsA(psz, pszData, cchData);
  268. #endif
  269. if (*pcchSize > 0)
  270. {
  271. if (*pcchSize <= cchData)
  272. {
  273. err = NO_ERROR;
  274. }
  275. else
  276. {
  277. // pcchSize returns the needed size
  278. err = ERROR_MORE_DATA;
  279. }
  280. }
  281. else
  282. err = GetLastError();
  283. LocalFree(psz);
  284. }
  285. return err;
  286. }
  287. #ifdef UNICODE
  288. #define NullTerminateRegSzString NullTerminateRegSzStringW
  289. #else
  290. #define NullTerminateRegSzString NullTerminateRegSzStringA
  291. #endif
  292. STDAPI_(LONG)
  293. NullTerminateRegSzString(
  294. IN OUT void * pvData, // data bytes returned from RegQueryValueEx()
  295. IN OUT DWORD * pcbData, // data size returned from RegQueryValueEx()
  296. IN DWORD cbDataBuffer, // data buffer size (actual allocated size of pvData)
  297. IN LONG lr) // long result returned from RegQueryValueEx()
  298. {
  299. ASSERT(pcbData != NULL); // Sanity check.
  300. if (lr == ERROR_SUCCESS && pvData != NULL)
  301. {
  302. DWORD cchDataBuffer = cbDataBuffer / sizeof(TCHAR); // cchDataBuffer is the actual allocated size of pvData in TCHARs
  303. DWORD cchData = *pcbData / sizeof(TCHAR); // cchData is the length of the string written into pvData in TCHARs (including the null terminator)
  304. PTSTR pszData = (PTSTR)pvData;
  305. DWORD cNullsMissing;
  306. ASSERT(cchDataBuffer >= cchData); // Sanity check.
  307. //
  308. // [1] string and size request with sufficient original buffer
  309. // (must ensure returned string and returned size include
  310. // null terminator)
  311. //
  312. cNullsMissing = cchData >= 1 && pszData[cchData-1] == TEXT('\0') ? 0 : 1;
  313. if (cNullsMissing > 0)
  314. {
  315. if (cchData + cNullsMissing <= cchDataBuffer)
  316. {
  317. pszData[cchData] = TEXT('\0');
  318. }
  319. else
  320. {
  321. lr = ERROR_MORE_DATA;
  322. }
  323. }
  324. *pcbData = (cchData + cNullsMissing) * sizeof(TCHAR);
  325. }
  326. else if ((lr == ERROR_SUCCESS && pvData == NULL) || lr == ERROR_MORE_DATA)
  327. {
  328. //
  329. // [2] size only request or string and size request with insufficient
  330. // original buffer (must ensure returned size includes null
  331. // terminator)
  332. //
  333. *pcbData += sizeof(TCHAR); // *** APPROXIMATION FOR PERF -- size is
  334. // therefore not guaranteed to be exact,
  335. // merely sufficient
  336. }
  337. return lr;
  338. }
  339. #ifdef UNICODE
  340. #define NullTerminateRegExpandSzString NullTerminateRegExpandSzStringW
  341. #else
  342. #define NullTerminateRegExpandSzString NullTerminateRegExpandSzStringA
  343. #endif
  344. STDAPI_(LONG)
  345. NullTerminateRegExpandSzString(
  346. IN HKEY hkey,
  347. IN PCTSTR pszValue,
  348. IN DWORD * pdwType,
  349. IN OUT void * pvData, // data bytes returned from RegQueryValueEx()
  350. IN OUT DWORD * pcbData, // data size returned from RegQueryValueEx()
  351. IN DWORD cbDataBuffer, // data buffer size (actual allocated size of pvData)
  352. IN LONG lr) // long result returned from RegQueryValueEx()
  353. {
  354. ASSERT(pdwType != NULL); // Sanity check.
  355. ASSERT(pcbData != NULL); // Sanity check.
  356. DWORD cbExpandDataBuffer;
  357. DWORD cbExpandData;
  358. void *pvExpandData;
  359. if (lr == ERROR_SUCCESS && pvData != NULL)
  360. {
  361. lr = NullTerminateRegSzString(pvData, pcbData, cbDataBuffer, lr);
  362. if (lr == ERROR_SUCCESS)
  363. {
  364. cbExpandDataBuffer = cbDataBuffer;
  365. cbExpandData = *pcbData;
  366. pvExpandData = pvData;
  367. }
  368. else
  369. {
  370. cbExpandDataBuffer = 0;
  371. cbExpandData = lr == ERROR_MORE_DATA ? *pcbData : 0;
  372. pvExpandData = pvData;
  373. }
  374. }
  375. else
  376. {
  377. cbExpandDataBuffer = 0;
  378. cbExpandData = (lr == ERROR_SUCCESS || lr == ERROR_MORE_DATA) ? *pcbData + sizeof(TCHAR) : 0;
  379. pvExpandData = NULL;
  380. }
  381. ASSERT(cbExpandData == 0 || cbExpandData >= sizeof(TCHAR)); // Sanity check.
  382. if (cbExpandData && !cbExpandDataBuffer)
  383. {
  384. DWORD cbTempBuffer = cbExpandData;
  385. DWORD cbTemp = cbExpandData;
  386. void *pvTemp = LocalAlloc(LPTR, cbTempBuffer);
  387. if (pvTemp)
  388. {
  389. if (pvExpandData)
  390. {
  391. ASSERT(lr == ERROR_MORE_DATA && pvData != NULL);
  392. memcpy(pvTemp, pvExpandData, cbExpandData - sizeof(TCHAR)); // zero-init of pvTemp automatically null-terminates
  393. }
  394. else
  395. {
  396. ASSERT(lr == ERROR_SUCCESS && pvData == NULL || lr == ERROR_MORE_DATA);
  397. DWORD dwTempType;
  398. if (RegQueryValueEx(hkey, pszValue, NULL, &dwTempType, (BYTE *)pvTemp, &cbTemp) != ERROR_SUCCESS
  399. || dwTempType != *pdwType
  400. || NullTerminateRegSzString(pvTemp, &cbTemp, cbTempBuffer, ERROR_SUCCESS) != ERROR_SUCCESS)
  401. {
  402. lr = ERROR_CAN_NOT_COMPLETE;
  403. LocalFree(pvTemp);
  404. pvTemp = NULL;
  405. }
  406. }
  407. if (pvTemp)
  408. {
  409. cbExpandDataBuffer = cbTempBuffer;
  410. cbExpandData = cbTemp;
  411. pvExpandData = pvTemp;
  412. }
  413. }
  414. else
  415. {
  416. lr = GetLastError();
  417. }
  418. }
  419. ASSERT(!cbExpandDataBuffer || (pvExpandData && cbExpandData)); // Sanity check.
  420. if (cbExpandDataBuffer)
  421. {
  422. ASSERT(lr == ERROR_SUCCESS || lr == ERROR_MORE_DATA); // Sanity check.
  423. DWORD lenExpandedData;
  424. lr = _ExpandRegString((PTSTR)pvExpandData, cbExpandDataBuffer / sizeof(TCHAR), &lenExpandedData);
  425. *pcbData = max(lenExpandedData * sizeof(TCHAR), cbExpandData);
  426. if (lr == ERROR_SUCCESS && *pcbData > cbDataBuffer && pvData)
  427. {
  428. lr = ERROR_MORE_DATA;
  429. }
  430. else if (lr == ERROR_MORE_DATA && pvData == NULL)
  431. {
  432. lr = ERROR_SUCCESS; // mimic RegQueryValueEx() convention
  433. } // for size only (pcbData) requests
  434. if (pvExpandData != pvData)
  435. {
  436. LocalFree(pvExpandData);
  437. }
  438. }
  439. else
  440. {
  441. ASSERT(lr != ERROR_SUCCESS && lr != ERROR_MORE_DATA); // Sanity check.
  442. }
  443. return lr;
  444. }
  445. #ifdef UNICODE
  446. #define NullTerminateRegMultiSzString NullTerminateRegMultiSzStringW
  447. #else
  448. #define NullTerminateRegMultiSzString NullTerminateRegMultiSzStringA
  449. #endif
  450. STDAPI_(LONG)
  451. NullTerminateRegMultiSzString(
  452. IN OUT void * pvData, // data bytes returned from RegQueryValueEx()
  453. IN OUT DWORD * pcbData, // data size returned from RegQueryValueEx()
  454. IN DWORD cbDataBuffer, // data buffer size (actual allocated size of pvData)
  455. IN LONG lr) // long result returned from RegQueryValueEx()
  456. {
  457. ASSERT(pcbData != NULL); // Sanity check.
  458. if (lr == ERROR_SUCCESS && pvData != NULL)
  459. {
  460. DWORD cchDataBuffer = cbDataBuffer / sizeof(TCHAR); // cchDataBuffer is the actual allocated size of pvData in TCHARs
  461. DWORD cchData = *pcbData / sizeof(TCHAR); // cchData is the length of the string written into pvData in TCHARs (including the null terminator)
  462. PTSTR pszData = (PTSTR)pvData;
  463. DWORD cNullsMissing;
  464. ASSERT(cchDataBuffer >= cchData); // Sanity check.
  465. //
  466. // [1] string and size request with sufficient original buffer
  467. // (must ensure returned string and returned size include
  468. // double null terminator)
  469. //
  470. if (cchData >= 2)
  471. {
  472. cNullsMissing = pszData[cchData-2]
  473. ? (pszData[cchData-1] ? 2 : 1)
  474. : (pszData[cchData-1] ? 1 : 0);
  475. }
  476. else
  477. {
  478. cNullsMissing = cchData == 1 && pszData[0] == TEXT('\0')
  479. ? 1
  480. : 2;
  481. }
  482. if (cchData + cNullsMissing <= cchDataBuffer)
  483. {
  484. for (DWORD i = 0; i < cNullsMissing; i++)
  485. {
  486. pszData[cchData+i] = TEXT('\0');
  487. }
  488. }
  489. else
  490. {
  491. lr = ERROR_MORE_DATA;
  492. }
  493. *pcbData = (cchData + cNullsMissing) * sizeof(TCHAR);
  494. }
  495. else if ((lr == ERROR_SUCCESS && pvData == NULL) || lr == ERROR_MORE_DATA)
  496. {
  497. //
  498. // [2] size only request or string and size request with insufficient
  499. // original buffer (must ensure returned size includes double
  500. // null terminator)
  501. //
  502. *pcbData += 2 * sizeof(TCHAR); // *** APPROXIMATION FOR PERF -- size
  503. // is therefore not guaranteed to be
  504. // exact, merely sufficient
  505. }
  506. return lr;
  507. }
  508. #ifdef UNICODE
  509. #define FixRegData FixRegDataW
  510. #else
  511. #define FixRegData FixRegDataA
  512. #endif
  513. STDAPI_(LONG)
  514. FixRegData(
  515. IN HKEY hkey,
  516. IN PCTSTR pszValue,
  517. IN SRRF dwFlags,
  518. IN DWORD * pdwType,
  519. IN OUT void * pvData, // data bytes returned from RegQueryValueEx()
  520. IN OUT DWORD * pcbData, // data size returned from RegQueryValueEx()
  521. IN DWORD cbDataBuffer, // data buffer size (actual allocated size of pvData)
  522. IN LONG lr) // long result returned from RegQueryValueEx()
  523. {
  524. switch (*pdwType)
  525. {
  526. case REG_SZ:
  527. if (pcbData)
  528. lr = NullTerminateRegSzString(pvData, pcbData, cbDataBuffer, lr);
  529. break;
  530. case REG_EXPAND_SZ:
  531. if (pcbData)
  532. lr = dwFlags & SRRF_NOEXPAND
  533. ? NullTerminateRegSzString(pvData, pcbData, cbDataBuffer, lr)
  534. : NullTerminateRegExpandSzString(hkey, pszValue, pdwType, pvData, pcbData, cbDataBuffer, lr);
  535. // Note:
  536. // If we automatically expand the REG_EXPAND_SZ data, we change
  537. // *pdwType to REG_SZ to reflect this fact. This helps to avoid
  538. // the situation where the caller could mistakenly re-expand it.
  539. if (!(dwFlags & SRRF_NOEXPAND))
  540. *pdwType = REG_SZ;
  541. break;
  542. case REG_MULTI_SZ:
  543. if (pcbData)
  544. lr = NullTerminateRegMultiSzString(pvData, pcbData, cbDataBuffer, lr);
  545. break;
  546. }
  547. return lr;
  548. }
  549. // SHExpandEnvironmentStrings
  550. //
  551. // In all cases, this returns a valid output buffer. The buffer may
  552. // be empty, or it may be truncated, but you can always use the string.
  553. //
  554. // RETURN VALUE:
  555. // 0 implies failure, either a truncated expansion or no expansion whatsoever
  556. // >0 implies complete expansion, value is count of characters written (excluding NULL)
  557. //
  558. DWORD WINAPI SHExpandEnvironmentStringsForUser(HANDLE hToken, PCTSTR pwzSrc, PTSTR pwzDst, DWORD cchSize)
  559. {
  560. DWORD dwRet;
  561. // 99/05/28 vtan: Handle specified users here. It's a Windows NT
  562. // thing only. Check for both conditions then load the function
  563. // dynamically out of userenv.dll. If the function cannot be
  564. // located or returns a problem default to the current user as
  565. // NULL hToken.
  566. if (hToken)
  567. {
  568. if (ExpandEnvironmentStringsForUser(hToken, pwzSrc, pwzDst, cchSize) != FALSE)
  569. {
  570. // userenv!ExpandEnvironmentStringsForUser returns
  571. // a BOOL result. Convert this to a DWORD result
  572. // that matches what kernel32!ExpandEnvironmentStrings
  573. // returns.
  574. dwRet = lstrlen(pwzDst) + sizeof('\0');
  575. }
  576. else
  577. {
  578. dwRet = 0;
  579. }
  580. }
  581. else
  582. {
  583. dwRet = ExpandEnvironmentStrings(pwzSrc, pwzDst, cchSize);
  584. }
  585. // The implementations of this function don't seem to gurantee gurantee certain
  586. // things about the output buffer in failure conditions that callers rely on.
  587. // So clean things up here.
  588. //
  589. // And I found code occasionally that handled semi-failure (success w/ dwRet>cchSize)
  590. // that assumed the string wasn't properly NULL terminated in this case. Fix that here
  591. // in the wrapper so our callers don't have to wig-out about errors.
  592. //
  593. // NOTE: we map all failures to 0 too.
  594. //
  595. if (dwRet > cchSize)
  596. {
  597. // Buffer too small, some code assumed there was still a string there and
  598. // tried to NULL terminate, do it for them.
  599. SHTruncateString(pwzDst, cchSize);
  600. dwRet = 0;
  601. }
  602. else if (dwRet == 0)
  603. {
  604. // Failure, assume no expansions...
  605. StrCpyN(pwzDst, pwzSrc, cchSize);
  606. }
  607. return dwRet;
  608. }
  609. DWORD WINAPI SHExpandEnvironmentStrings(LPCTSTR pwzSrc, LPTSTR pwzDst, DWORD cchSize)
  610. {
  611. return(SHExpandEnvironmentStringsForUser(NULL, pwzSrc, pwzDst, cchSize));
  612. }