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.

508 lines
15 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. #ifdef _X86_
  5. #include <w95wraps.h>
  6. #endif
  7. BOOL _PathAppend(LPCTSTR pszBase, LPCTSTR pszAppend, LPTSTR pszOut, DWORD cchOut)
  8. {
  9. DWORD cchBase = lstrlen(pszBase);
  10. // +1 is one for the whack
  11. if (cchOut > cchBase + lstrlen(pszAppend) + 1)
  12. {
  13. StrCpy(pszOut, pszBase);
  14. pszOut+=cchBase;
  15. *pszOut++ = TEXT('\\');
  16. StrCpy(pszOut, pszAppend);
  17. return TRUE;
  18. }
  19. return FALSE;
  20. }
  21. LWSTDAPI AssocMakeFileExtsToApplication(ASSOCMAKEF flags, LPCTSTR pszExt, LPCTSTR pszApplication)
  22. {
  23. RIP(!IsOS(OS_WHISTLERORGREATER));
  24. DWORD err = ERROR_SUCCESS;
  25. WCHAR sz[MAX_PATH];
  26. SHTCharToUnicode(pszExt, sz, ARRAYSIZE(sz));
  27. HKEY hk = SHGetShellKey(SHELLKEY_HKCU_FILEEXTS, sz, pszApplication != NULL);
  28. if (hk)
  29. {
  30. if (pszApplication)
  31. {
  32. err = SHSetValue(hk, NULL, TEXT("Application"),
  33. REG_SZ, pszApplication, CbFromCch(lstrlen(pszApplication) +1));
  34. }
  35. else // we should always clear
  36. err = SHDeleteValue(hk, NULL, TEXT("Application"));
  37. RegCloseKey(hk);
  38. }
  39. else
  40. err = GetLastError();
  41. return HRESULT_FROM_WIN32(err);
  42. }
  43. HRESULT _AllocValueString(HKEY hkey, LPCTSTR pszKey, LPCTSTR pszVal, LPTSTR *ppsz)
  44. {
  45. DWORD cb, err;
  46. err = SHGetValue(hkey, pszKey, pszVal, NULL, NULL, &cb);
  47. ASSERT(ppsz);
  48. *ppsz = NULL;
  49. if (NOERROR == err)
  50. {
  51. LPTSTR psz = (LPTSTR) LocalAlloc(LPTR, cb);
  52. if (psz)
  53. {
  54. err = SHGetValue(hkey, pszKey, pszVal, NULL, (LPVOID)psz, &cb);
  55. if (NOERROR == err)
  56. *ppsz = psz;
  57. else
  58. LocalFree(psz);
  59. }
  60. else
  61. err = ERROR_OUTOFMEMORY;
  62. }
  63. return HRESULT_FROM_WIN32(err);
  64. }
  65. // <Swipped from the NT5 version of Shell32>
  66. #define SZ_REGKEY_FILEASSOCIATION TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileAssociation")
  67. LWSTDAPI_(void) PrettifyFileDescription(LPTSTR pszDesc, LPCTSTR pszCutList)
  68. {
  69. LPTSTR pszCutListReg;
  70. if (!pszDesc || !*pszDesc)
  71. return;
  72. // get the Cut list from registry
  73. // this is MULTI_SZ
  74. if (S_OK == _AllocValueString(HKEY_LOCAL_MACHINE, SZ_REGKEY_FILEASSOCIATION, TEXT("CutList"), &pszCutListReg))
  75. {
  76. pszCutList = pszCutListReg;
  77. }
  78. if (pszCutList)
  79. {
  80. // cut strings in cut list from file description
  81. for (LPCTSTR pszCut = pszCutList; *pszCut; pszCut = pszCut + lstrlen(pszCut) + 1)
  82. {
  83. LPTSTR pch = StrRStrI(pszDesc, NULL, pszCut);
  84. // cut the exact substring from the end of file description
  85. if (pch && !*(pch + lstrlen(pszCut)))
  86. {
  87. *pch = '\0';
  88. // remove trailing spaces
  89. for (--pch; (pch >= pszDesc) && (TEXT(' ') == *pch); pch--)
  90. *pch = 0;
  91. break;
  92. }
  93. }
  94. if (pszCutListReg)
  95. LocalFree(pszCutListReg);
  96. }
  97. }
  98. /*
  99. <Swipped from the NT5 version of Shell32>
  100. GetFileDescription retrieves the friendly name from a file's verion rsource.
  101. The first language we try will be the first item in the
  102. "\VarFileInfo\Translations" section; if there's nothing there,
  103. we try the one coded into the IDS_VN_FILEVERSIONKEY resource string.
  104. If we can't even load that, we just use English (040904E4). We
  105. also try English with a null codepage (04090000) since many apps
  106. were stamped according to an old spec which specified this as
  107. the required language instead of 040904E4.
  108. If there is no FileDescription in version resource, return the file name.
  109. Parameters:
  110. LPCTSTR pszPath: full path of the file
  111. LPTSTR pszDesc: pointer to the buffer to receive friendly name. If NULL,
  112. *pcchDesc will be set to the length of friendly name in
  113. characters, including ending NULL, on successful return.
  114. UINT *pcchDesc: length of the buffer in characters. On successful return,
  115. it contains number of characters copied to the buffer,
  116. including ending NULL.
  117. Return:
  118. TRUE on success, and FALSE otherwise
  119. */
  120. BOOL WINAPI SHGetFileDescription(LPCTSTR pszPath, LPCTSTR pszVersionKeyIn, LPCTSTR pszCutListIn, LPTSTR pszDesc, UINT *pcchDesc)
  121. {
  122. UINT cchValue = 0;
  123. TCHAR szPath[MAX_PATH], *pszValue = NULL;
  124. DWORD dwAttribs;
  125. DWORD dwHandle; /* version subsystem handle */
  126. DWORD dwVersionSize; /* size of the version data */
  127. LPTSTR lpVersionBuffer = NULL; /* pointer to version data */
  128. TCHAR szVersionKey[60]; /* big enough for anything we need */
  129. struct _VERXLATE
  130. {
  131. WORD wLanguage;
  132. WORD wCodePage;
  133. } *lpXlate; /* ptr to translations data */
  134. ASSERT(pszPath && *pszPath && pcchDesc);
  135. if (!PathFileExistsAndAttributes(pszPath, &dwAttribs))
  136. {
  137. return FALSE;
  138. }
  139. // copy the path to the dest dir
  140. lstrcpyn(szPath, pszPath, ARRAYSIZE(szPath));
  141. if ((dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ||
  142. PathIsUNCServer(pszPath) ||
  143. PathIsUNCServerShare(pszPath))
  144. {
  145. // bail in the \\server, \\server\share, and directory case or else GetFileVersionInfo() will try
  146. // to do a LoadLibraryEx() on the path (which will fail, but not before we seach the entire include
  147. // path which can take a long time)
  148. goto Exit;
  149. }
  150. dwVersionSize = GetFileVersionInfoSize(szPath, &dwHandle);
  151. if (dwVersionSize == 0L)
  152. goto Exit; /* no version info */
  153. lpVersionBuffer = (LPTSTR)LocalAlloc(LPTR, dwVersionSize);
  154. if (lpVersionBuffer == NULL)
  155. goto Exit;
  156. if (!GetFileVersionInfo(szPath, dwHandle, dwVersionSize, lpVersionBuffer))
  157. goto Exit;
  158. // Try same language as the caller
  159. if (pszVersionKeyIn)
  160. {
  161. lstrcpyn(szVersionKey, pszVersionKeyIn, ARRAYSIZE(szVersionKey));
  162. if (VerQueryValue(lpVersionBuffer, szVersionKey, (void **)&pszValue, &cchValue))
  163. {
  164. goto Exit;
  165. }
  166. }
  167. // Try first language this supports
  168. // Look for translations
  169. if (VerQueryValue(lpVersionBuffer, TEXT("\\VarFileInfo\\Translation"),
  170. (void **)&lpXlate, &cchValue)
  171. && cchValue)
  172. {
  173. wnsprintf(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\%04X%04X\\FileDescription"),
  174. lpXlate[0].wLanguage, lpXlate[0].wCodePage);
  175. if (VerQueryValue(lpVersionBuffer, szVersionKey, (void **)&pszValue, &cchValue))
  176. goto Exit;
  177. }
  178. #ifdef UNICODE
  179. // try English, unicode code page
  180. lstrcpy(szVersionKey, TEXT("\\StringFileInfo\\040904B0\\FileDescription"));
  181. if (VerQueryValue(lpVersionBuffer, szVersionKey, (void **)&pszValue, &cchValue))
  182. goto Exit;
  183. #endif
  184. // try English
  185. lstrcpy(szVersionKey, TEXT("\\StringFileInfo\\040904E4\\FileDescription"));
  186. if (VerQueryValue(lpVersionBuffer, szVersionKey, (void **)&pszValue, &cchValue))
  187. goto Exit;
  188. // try English, null codepage
  189. lstrcpy(szVersionKey, TEXT("\\StringFileInfo\\04090000\\FileDescription"));
  190. if (VerQueryValue(lpVersionBuffer, szVersionKey, (void **)&pszValue, &cchValue))
  191. goto Exit;
  192. Exit:
  193. if (!pszValue || !*pszValue)
  194. {
  195. // Could not find FileVersion info in a reasonable format, return file name
  196. PathRemoveExtension(szPath);
  197. pszValue = PathFindFileName(szPath);
  198. cchValue = lstrlen(pszValue);
  199. }
  200. PrettifyFileDescription(pszValue, pszCutListIn);
  201. cchValue = lstrlen(pszValue) + 1;
  202. if (!pszDesc) // only want to know the length of the friendly name
  203. *pcchDesc = cchValue;
  204. else
  205. {
  206. *pcchDesc = min(*pcchDesc, cchValue);
  207. lstrcpyn(pszDesc, pszValue, *pcchDesc);
  208. }
  209. if (lpVersionBuffer)
  210. LocalFree(lpVersionBuffer);
  211. return TRUE;
  212. }
  213. // Convert LPTSTR to LPSTR and return TRUE if the LPSTR can
  214. // be converted back to LPTSTR without unacceptible data loss
  215. //
  216. BOOL DoesStringRoundTrip(LPCTSTR pwszIn, LPSTR pszOut, UINT cchOut)
  217. {
  218. #ifdef UNICODE
  219. // On NT5 we have to be more stringent since you can switch UI
  220. // languages on the fly, thereby breaking this constant codepage
  221. // assumption inherent in the downlevel implementations.
  222. //
  223. // we have to support the function being called with a null pszOut
  224. // just to determine if pwszIn will roundtrip
  225. //
  226. if (g_bRunningOnNT5OrHigher)
  227. {
  228. LPCTSTR pIn = pwszIn;
  229. LPSTR pOut = pszOut;
  230. UINT cch = cchOut;
  231. while (*pIn)
  232. {
  233. if (*pIn > ((TCHAR)127))
  234. {
  235. if (cchOut) // caller has provided a buffer
  236. {
  237. #ifdef DEBUG
  238. SHUnicodeToAnsiCP(CP_ACPNOVALIDATE, pwszIn, pszOut, cchOut);
  239. #else
  240. SHUnicodeToAnsi(pwszIn, pszOut, cchOut);
  241. #endif
  242. }
  243. return FALSE;
  244. }
  245. if (cch) // we have a buffer and it still has space
  246. {
  247. *pOut++ = (char)*pIn;
  248. if (!--cch)
  249. {
  250. break; // out buffer filled, leave.
  251. }
  252. }
  253. pIn++;
  254. }
  255. // Null terminate the out buffer
  256. if (cch)
  257. {
  258. *pOut = '\0';
  259. }
  260. else if (cchOut)
  261. {
  262. *(pOut-1) = '\0';
  263. }
  264. // Everything was low ascii, no dbcs worries and it will always round-trip
  265. return TRUE;
  266. }
  267. else
  268. // but we probably don't want to change downlevel shell behavior
  269. // in this regard, so we keep that implementation:
  270. //
  271. {
  272. BOOL fRet = FALSE;
  273. WCHAR wszTemp[MAX_PATH];
  274. LPWSTR pwszTemp = wszTemp;
  275. UINT cchTemp = ARRAYSIZE(wszTemp);
  276. // We better have enough room for the buffer.
  277. if (ARRAYSIZE(wszTemp) < cchOut)
  278. {
  279. pwszTemp = (LPWSTR)LocalAlloc(LPTR, cchOut*sizeof(WCHAR));
  280. cchTemp = cchOut;
  281. }
  282. if (pwszTemp)
  283. {
  284. #ifdef DEBUG
  285. SHUnicodeToAnsiCP(CP_ACPNOVALIDATE, pwszIn, pszOut, cchOut);
  286. #else
  287. SHUnicodeToAnsi(pwszIn, pszOut, cchOut);
  288. #endif
  289. SHAnsiToUnicode(pszOut, pwszTemp, cchTemp);
  290. fRet = StrCmpCW(pwszIn, pwszTemp) == 0; // are they the same?
  291. if (pwszTemp != wszTemp)
  292. {
  293. LocalFree(pwszTemp);
  294. }
  295. }
  296. return fRet;
  297. }
  298. #else
  299. StrCpyN(pszOut, pwszIn, cchOut);
  300. return TRUE;
  301. #endif
  302. }
  303. DWORD _ExpandRegString(PTSTR pszData, DWORD cchData, DWORD *pcchSize)
  304. {
  305. DWORD err = ERROR_OUTOFMEMORY;
  306. PTSTR psz = StrDup(pszData);
  307. if (psz)
  308. {
  309. // now we will try to expand back into the target buffer
  310. // NOTE we deliberately dont use SHExpandEnvironmentStrings
  311. // since it will not give us the size we need
  312. // we have to use
  313. #ifdef UNICODE
  314. *pcchSize = ExpandEnvironmentStringsW(psz, pszData, cchData);
  315. #else
  316. *pcchSize = ExpandEnvironmentStringsA(psz, pszData, cchData);
  317. #endif
  318. if (*pcchSize > 0)
  319. {
  320. if (*pcchSize <= cchData)
  321. {
  322. err = NO_ERROR;
  323. }
  324. else
  325. {
  326. // pcchSize returns the needed size
  327. err = ERROR_MORE_DATA;
  328. }
  329. }
  330. else
  331. err = GetLastError();
  332. LocalFree(psz);
  333. }
  334. return err;
  335. }
  336. DWORD _SizeExpandString(HKEY hk, PCTSTR pszValue, void *pvData, DWORD *pcbSize)
  337. {
  338. DWORD err = ERROR_OUTOFMEMORY;
  339. // *pcbSize is the size required by RegQueryValueEx
  340. // Find out the length of the expanded string
  341. // we have to call in and actually get the data to do this
  342. PTSTR psz;
  343. if (SUCCEEDED(SHLocalAlloc(*pcbSize + sizeof(TCHAR), &psz)))
  344. {
  345. err = RegQueryValueEx(hk, pszValue, NULL, NULL, (LPBYTE)psz, pcbSize);
  346. // ASSERT(err != ERROR_MORE_DATA);
  347. if (NO_ERROR == err)
  348. {
  349. TCHAR szBuff[1];
  350. // insure NULL termination with our extra char
  351. psz[*pcbSize/sizeof(TCHAR)] = 0;
  352. #ifdef UNICODE
  353. DWORD cbExpand = CbFromCchW(ExpandEnvironmentStringsW(psz, szBuff, ARRAYSIZE(szBuff)));
  354. #else
  355. DWORD cbExpand = CbFromCchA(ExpandEnvironmentStringsA(psz, szBuff, ARRAYSIZE(szBuff)));
  356. #endif
  357. if (cbExpand > *pcbSize)
  358. *pcbSize = cbExpand;
  359. }
  360. LocalFree(psz);
  361. // if pvData is NULL then we return success (caller is sizing)
  362. // if pvData is non-NULL we need to return ERROR_MORE_DATA
  363. if (err == NO_ERROR && pvData)
  364. err = ERROR_MORE_DATA;
  365. }
  366. return err;
  367. }
  368. #ifdef UNICODE
  369. #define FixupRegString FixupRegStringW
  370. #else
  371. #define FixupRegString FixupRegStringA
  372. #endif
  373. STDAPI_(DWORD) FixupRegString(HKEY hk, PCTSTR pszValue, BOOL fExpand, DWORD err, void *pvData, DWORD *pcbData, DWORD *pcbSize)
  374. {
  375. BOOL fNeedsSize = FALSE;
  376. if ((NO_ERROR == err && pvData))
  377. {
  378. PTSTR pszData = (PTSTR)pvData;
  379. DWORD cchSize = *pcbSize / sizeof(TCHAR);
  380. DWORD cchData = *pcbData / sizeof(TCHAR);
  381. // Note: on Win95, RegSetValueEx will always write the
  382. // full string out, including the null terminator. On NT,
  383. // it won't unless the right length was specified.
  384. // Hence, we have the following check for termination
  385. // FRINGE: if you get back exactly the size you asked for
  386. // and its not NULL terminated, then we need to
  387. // treat it like ERROR_MORE_DATA
  388. // if the value was empty, treat it like a non-terminated string
  389. if (!cchSize || pszData[cchSize - 1])
  390. {
  391. // this string was not NULL terminated
  392. if (cchData > cchSize)
  393. {
  394. // NULL terminate just in case
  395. pszData[cchSize++] = 0;
  396. }
  397. else
  398. {
  399. ASSERT(cchData == cchSize);
  400. fNeedsSize = TRUE;
  401. }
  402. }
  403. if (!fNeedsSize)
  404. {
  405. // this will expand if necessary and
  406. // if the expand overflows, return ERROR_MORE_DATA
  407. if (fExpand)
  408. err = _ExpandRegString(pszData, cchData, &cchSize);
  409. *pcbSize = CbFromCch(cchSize);
  410. }
  411. }
  412. else if (pcbData && (err == ERROR_MORE_DATA || (NO_ERROR == err && !pvData)))
  413. {
  414. // we need to calculate if:
  415. // 1. the RegQueryValueEx() says there is not enough room
  416. // 2. the caller is requesting the size
  417. fNeedsSize = TRUE;
  418. }
  419. if (fNeedsSize )
  420. {
  421. if (fExpand)
  422. {
  423. err = _SizeExpandString(hk, pszValue, pvData, pcbSize);
  424. }
  425. else
  426. {
  427. pcbSize += sizeof(TCHAR);
  428. err = pvData ? ERROR_MORE_DATA : NO_ERROR;
  429. }
  430. }
  431. return err;
  432. }