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.

650 lines
22 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // File: inistr.cpp
  4. //
  5. // Contents: SHGet/SetIniStringW implementations, which save strings into
  6. // INI files in a manner that survives the round trip to disk.
  7. //
  8. //----------------------------------------------------------------------------
  9. #include "priv.h"
  10. #define _SHELL32_
  11. #define _SHDOCVW_
  12. #include "unicwrap.h"
  13. #include <platform.h>
  14. #include <mlang.h>
  15. //
  16. // Do this in every wrapper function that has an output parameter.
  17. // It raises assertion failures on the main code path so that
  18. // the same assertions are raised on NT and 95. The CStrOut class
  19. // doesn't like it when you say that an output buffer is NULL yet
  20. // has nonzero length. Without this macro, the bug would go undetected
  21. // on NT and appear only on Win95.
  22. //
  23. #define VALIDATE_OUTBUF(s, cch) ASSERT((s) != NULL || (cch) == 0)
  24. //----------------------------------------------------------------------------
  25. //
  26. // The basic problem is that INI files are ANSI-only, so any UNICODE
  27. // string you put into it won't round-trip.
  28. //
  29. // So the solution is to record UNICODE strings in UTF7. Why UTF7?
  30. // Because we can't use UTF8, since XxxPrivateProfileStringW will try
  31. // to convert the 8-bit values to/from UNICODE and mess them up. Since
  32. // some of the 8-bit values might not even be valid (e.g., a DBCS lead
  33. // byte followed by an illegal trail byte), we cannot assume that the
  34. // string will survive the ANSI -> UNICODE -> ANSI round-trip.
  35. //
  36. // The UTF7 string is stored in a [Section.W] section, under the
  37. // same key name. The original ANSI string is stored in a [Section.A]
  38. // section, again, with the same key name.
  39. //
  40. // (We separate the A/W from the section name with a dot so it is less
  41. // likely that we will accidentally collide with other section names.
  42. //
  43. // We store the original ANSI string twice so we can compare the two
  44. // and see if a downlevel app (e.g., IE4) has changed the [Section]
  45. // version. If so, then we ignore the [Section.W] version since it's stale.
  46. //
  47. // If the original string is already 7-bit clean, then no UTF7 string is
  48. // recorded.
  49. //
  50. BOOL
  51. Is7BitClean(LPCWSTR pwsz)
  52. {
  53. for ( ; *pwsz; pwsz++) {
  54. if ((UINT)*pwsz > 127)
  55. return FALSE;
  56. }
  57. return TRUE;
  58. }
  59. //----------------------------------------------------------------------------
  60. //
  61. // Yet another conversion class -- this one is for creating the
  62. // variants of a section name.
  63. //
  64. // Note! Since INI files are ASCII, section names are necessarily 7-bit
  65. // clean, so we can cheat a lot of stuff.
  66. //
  67. class CStrSectionX : public CConvertStrW
  68. {
  69. public:
  70. CStrSectionX(LPCWSTR pwszSection);
  71. };
  72. //
  73. // We append a dot an an A or W to the section name.
  74. //
  75. #define SECTION_SUFFIX_LEN 2
  76. CStrSectionX::CStrSectionX(LPCWSTR pwszSection)
  77. {
  78. ASSERT(_pwstr == NULL);
  79. if (pwszSection) {
  80. ASSERT(Is7BitClean(pwszSection));
  81. UINT cwchNeeded = lstrlenW(pwszSection) + SECTION_SUFFIX_LEN + 1;
  82. if (cwchNeeded > ARRAYSIZE(_awch)) {
  83. _pwstr = new WCHAR[cwchNeeded];
  84. } else {
  85. _pwstr = _awch;
  86. }
  87. if (_pwstr) {
  88. // Build the string initially with ".A" stuck on the end
  89. // It will later get changed to a ".W"
  90. lstrcpyW(_pwstr, pwszSection);
  91. lstrcatW(_pwstr, L".A");
  92. }
  93. }
  94. }
  95. //----------------------------------------------------------------------------
  96. //
  97. // Mini-class for keeping track of UTF7 strings. These are kept in ANSI
  98. // most of the time since that's what ConvertINetUnicodeToMultiByte uses.
  99. //
  100. // The UTF7 shadow is prefixed by a checksum of the original string, which
  101. // we use on read-back to see if the shadow still corresponds to the
  102. // original string.
  103. //
  104. class CStrUTF7 : public CConvertStr
  105. {
  106. public:
  107. inline CStrUTF7() : CConvertStr(CP_ACP) { };
  108. void SetUnicode(LPCWSTR pwszValue);
  109. };
  110. //
  111. // Note that this can be slow since it happens only when we encounter
  112. // a non-ANSI character.
  113. //
  114. void CStrUTF7::SetUnicode(LPCWSTR pwszValue)
  115. {
  116. int cwchLen = lstrlenW(pwszValue);
  117. HRESULT hres;
  118. DWORD dwMode;
  119. int cwchLenT = cwchLen;
  120. // Save room for terminating NULL. We must convert the NULL separately
  121. // because UTF7 does not translate NULL to NULL.
  122. int cchNeeded = ARRAYSIZE(_ach) - 1;
  123. dwMode = 0;
  124. hres = ConvertINetUnicodeToMultiByte(&dwMode, CP_UTF7, pwszValue,
  125. &cwchLenT, _ach,
  126. &cchNeeded);
  127. if (SUCCEEDED(hres)) {
  128. ASSERT(cchNeeded + 1 <= ARRAYSIZE(_ach));
  129. _pstr = _ach;
  130. } else {
  131. _pstr = new CHAR[cchNeeded + 1];
  132. if (!_pstr)
  133. return; // No string - tough
  134. cwchLenT = cwchLen;
  135. dwMode = 0;
  136. hres = ConvertINetUnicodeToMultiByte(&dwMode, CP_UTF7, pwszValue,
  137. &cwchLenT, _pstr,
  138. &cchNeeded);
  139. if (FAILED(hres)) { // Couldn't convert - tough
  140. Free();
  141. return;
  142. }
  143. }
  144. // Terminate explicitly since UTF7 doesn't.
  145. _pstr[cchNeeded] = '\0';
  146. }
  147. //
  148. // pwszSection = section name into which to write pwszValue (UNICODE)
  149. // pwszSectionA = section name into which to write ANSI shadow
  150. // pwszKey = key name for both pwszValue and strUTF7
  151. // pwszFileName = file name
  152. //
  153. // pwszSectionA can be NULL if a low-memory condition was encountered.
  154. //
  155. // strUTF7 can be NULL, meaning that the shadows should be deleted.
  156. //
  157. // Write pwszSection first, followed by pwszSectionA, then pwszSectionW.
  158. // This ensures that the backwards-compatibility string comes first in
  159. // the file, in case there are apps that assume such.
  160. //
  161. // pwszSectionW is computed from pwszSectionA by changing the last "A"
  162. // to a "W". pwszSecionW gets the UTF7-encoded unicode string.
  163. // strUTF7 might be NULL, meaning that we should delete the shadow strings.
  164. //
  165. BOOL WritePrivateProfileStringMultiW(LPCWSTR pwszSection, LPCWSTR pwszValue,
  166. LPWSTR pwszSectionA, CStrUTF7& strUTF7,
  167. LPCWSTR pwszKey, LPCWSTR pwszFileName)
  168. {
  169. BOOL fRc = WritePrivateProfileStringW(pwszSection, pwszKey, pwszValue, pwszFileName);
  170. if (pwszSectionA) {
  171. //
  172. // Write the [Section.A] key, or delete it if there is no UTF7.
  173. //
  174. WritePrivateProfileStringW(pwszSectionA, pwszKey,
  175. strUTF7 ? pwszValue : NULL, pwszFileName);
  176. //
  177. // Now change pwszSectionA to pwszSectionW so we can write out
  178. // the UTF7 encoding.
  179. //
  180. pwszSectionA[lstrlenW(pwszSectionA) - 1] = TEXT('W');
  181. CStrInW strUTF7W(strUTF7);
  182. // This really writes [Section.W]
  183. WritePrivateProfileStringW(pwszSectionA, pwszKey, strUTF7W, pwszFileName);
  184. }
  185. return fRc;
  186. }
  187. BOOL WritePrivateProfileStringMultiA(LPCWSTR pwszSection, LPCWSTR pwszValue,
  188. LPWSTR pwszSectionA, CStrUTF7& strUTF7,
  189. LPCWSTR pwszKey, LPCWSTR pwszFileName)
  190. {
  191. CStrIn strSection(pwszSection);
  192. CStrIn strSectionA(pwszSectionA);
  193. CStrIn strValue(pwszValue);
  194. CStrIn strKey(pwszKey);
  195. CPPFIn strFileName(pwszFileName); // PrivateProfile filename needs special class
  196. BOOL fRc = WritePrivateProfileStringA(strSection, strKey, strValue, strFileName);
  197. if ((LPSTR)strSectionA) {
  198. //
  199. // Write the [Section.A] key, or delete it if there is no UTF7.
  200. //
  201. WritePrivateProfileStringA(strSectionA, strKey,
  202. #if defined(UNIX) && !defined(ux10)
  203. strUTF7 ? (LPSTR)strValue : (LPSTR)NULL, strFileName);
  204. #else
  205. strUTF7 ? strValue : NULL, strFileName);
  206. #endif
  207. //
  208. // Now change strSectionA to strSectionW so we can write out
  209. // the UTF7 encoding.
  210. //
  211. strSectionA[lstrlenA(strSectionA) - 1] = 'W';
  212. // This really writes [Section.W]
  213. WritePrivateProfileStringA(strSectionA, strKey, strUTF7, strFileName);
  214. }
  215. return fRc;
  216. }
  217. BOOL WINAPI
  218. SHSetIniStringW(LPCWSTR pwszSection, LPCWSTR pwszKey, LPCWSTR pwszValue, LPCWSTR pwszFileName)
  219. {
  220. // We have no way of encoding these two, so they had better by 7-bit clean
  221. // We also do not support "delete entire section"
  222. AssertMsg(pwszSection != NULL,
  223. TEXT("SHSetIniStringW: Section name cannot be NULL; bug in caller"));
  224. AssertMsg(Is7BitClean(pwszSection),
  225. TEXT("SHSetIniStringW: Section name not 7-bit clean; bug in caller"));
  226. AssertMsg(pwszKey != NULL,
  227. TEXT("SHSetIniStringW: Key name cannot be NULL; bug in caller"));
  228. AssertMsg(!pwszKey || Is7BitClean(pwszKey),
  229. TEXT("SHSetIniStringW: Key name not 7-bit clean; bug in caller"));
  230. CStrSectionX strSectionX(pwszSection);
  231. CStrUTF7 strUTF7; // Assume no UTF7 needed.
  232. if (strSectionX && pwszKey && pwszValue && !Is7BitClean(pwszValue)) {
  233. //
  234. // The value is not 7-bit clean. Must create a UTF7 version.
  235. //
  236. strUTF7.SetUnicode(pwszValue);
  237. }
  238. if (g_bRunningOnNT)
  239. return WritePrivateProfileStringMultiW(pwszSection, pwszValue,
  240. strSectionX, strUTF7,
  241. pwszKey, pwszFileName);
  242. else
  243. return WritePrivateProfileStringMultiA(pwszSection, pwszValue,
  244. strSectionX, strUTF7,
  245. pwszKey, pwszFileName);
  246. }
  247. //
  248. // Keep calling GetPrivateProfileString with bigger and bigger buffers
  249. // until we get the entire string. Start with MAX_PATH, since that's
  250. // usually plenty big enough.
  251. //
  252. // The returned buffer must be freed with LocalFree, not delete[].
  253. //
  254. LPVOID GetEntirePrivateProfileStringAorW(
  255. LPCVOID pszSection,
  256. LPCVOID pszKey,
  257. LPCVOID pszFileName,
  258. BOOL fUnicode)
  259. {
  260. int CharSize = fUnicode ? sizeof(WCHAR) : sizeof(CHAR);
  261. UINT cchResult = MAX_PATH;
  262. LPVOID pszResult = LocalAlloc(LMEM_FIXED, cchResult * CharSize);
  263. LPVOID pszFree = pszResult;
  264. while (pszResult) {
  265. UINT cchRc;
  266. if (fUnicode)
  267. cchRc = GetPrivateProfileStringW((LPCWSTR)pszSection,
  268. (LPCWSTR)pszKey,
  269. L"",
  270. (LPWSTR)pszResult, cchResult,
  271. (LPCWSTR)pszFileName);
  272. else
  273. cchRc = GetPrivateProfileStringA((LPCSTR)pszSection,
  274. (LPCSTR)pszKey,
  275. "",
  276. (LPSTR)pszResult, cchResult,
  277. (LPCSTR)pszFileName);
  278. if (cchRc < cchResult - 1)
  279. return pszResult;
  280. // Buffer too small - iterate
  281. cchResult *= 2;
  282. LPVOID pszNew = LocalReAlloc(pszResult, cchResult * CharSize, LMEM_MOVEABLE);
  283. pszFree = pszResult;
  284. pszResult = pszNew;
  285. }
  286. //
  287. // Memory allocation failed; free pszFree while we still can.
  288. //
  289. if (pszFree)
  290. LocalFree(pszFree);
  291. return NULL;
  292. }
  293. DWORD GetPrivateProfileStringMultiW(LPCWSTR pwszSection, LPCWSTR pwszKey,
  294. LPWSTR pwszSectionA,
  295. LPWSTR pwszReturnedString, DWORD cchSize,
  296. LPCWSTR pwszFileName)
  297. {
  298. LPWSTR pwszValue = NULL;
  299. LPWSTR pwszValueA = NULL;
  300. LPWSTR pwszUTF7 = NULL;
  301. DWORD dwRc;
  302. pwszValue = (LPWSTR)GetEntirePrivateProfileStringAorW(
  303. pwszSection, pwszKey,
  304. pwszFileName, TRUE);
  305. if (pwszValue) {
  306. //
  307. // If the value is an empty string, then don't waste your
  308. // time trying to get the UNICODE version - the UNICODE version
  309. // of the empty string is the empty string.
  310. //
  311. // Otherwise, get the ANSI shadow hidden in [Section.A]
  312. // and see if it matches. If not, then the file was edited
  313. // by a downlevel app and we should just use pwszValue after all.
  314. if (pwszValue[0] &&
  315. (pwszValueA = (LPWSTR)GetEntirePrivateProfileStringAorW(
  316. pwszSectionA, pwszKey,
  317. pwszFileName, TRUE)) != NULL &&
  318. lstrcmpW(pwszValue, pwszValueA) == 0) {
  319. // our shadow is still good - run with it
  320. // Change [Section.A] to [Section.W]
  321. pwszSectionA[lstrlenW(pwszSectionA) - 1] = TEXT('W');
  322. pwszUTF7 = (LPWSTR)GetEntirePrivateProfileStringAorW(
  323. pwszSectionA, pwszKey,
  324. pwszFileName, TRUE);
  325. CStrIn strUTF7(pwszUTF7);
  326. dwRc = 0; // Assume something goes wrong
  327. if (strUTF7) {
  328. dwRc = SHAnsiToUnicodeCP(CP_UTF7, strUTF7, pwszReturnedString, cchSize);
  329. }
  330. if (dwRc == 0) {
  331. // Problem converting to UTF7 - just use the ANSI version
  332. dwRc = SHUnicodeToUnicode(pwszValue, pwszReturnedString, cchSize);
  333. }
  334. } else {
  335. // String was empty or couldn't get [Section.A] shadow or
  336. // shadow doesn't match. Just use the regular string.
  337. dwRc = SHUnicodeToUnicode(pwszValue, pwszReturnedString, cchSize);
  338. }
  339. // The SHXxxToYyy functions include the terminating zero,
  340. // which we want to exclude.
  341. if (dwRc > 0)
  342. dwRc--;
  343. } else {
  344. // Fatal error reading values from file; just use the boring API
  345. dwRc = GetPrivateProfileStringW(pwszSection,
  346. pwszKey,
  347. L"",
  348. pwszReturnedString, cchSize,
  349. pwszFileName);
  350. }
  351. if (pwszValue)
  352. LocalFree(pwszValue);
  353. if (pwszValueA)
  354. LocalFree(pwszValueA);
  355. if (pwszUTF7)
  356. LocalFree(pwszUTF7);
  357. return dwRc;
  358. }
  359. DWORD GetPrivateProfileStringMultiA(LPCWSTR pwszSection, LPCWSTR pwszKey,
  360. LPWSTR pwszSectionA,
  361. LPWSTR pwszReturnedString, DWORD cchSize,
  362. LPCWSTR pwszFileName)
  363. {
  364. CStrIn strSection(pwszSection);
  365. CStrIn strSectionA(pwszSectionA);
  366. CStrIn strKey(pwszKey);
  367. CPPFIn strFileName(pwszFileName); // PrivateProfile filename needs special class
  368. LPSTR pszValue = NULL;
  369. LPSTR pszValueA = NULL;
  370. LPSTR pszUTF7 = NULL;
  371. DWORD dwRc;
  372. if (strSectionA &&
  373. (pszValue = (LPSTR)GetEntirePrivateProfileStringAorW(
  374. strSection, strKey,
  375. strFileName, FALSE)) != NULL) {
  376. //
  377. // If the value is an empty string, then don't waste your
  378. // time trying to get the UNICODE version - the UNICODE version
  379. // of the empty string is the empty string.
  380. //
  381. // Otherwise, get the ANSI shadow hidden in [Section.A]
  382. // and see if it matches. If not, then the file was edited
  383. // by a downlevel app and we should just use pwszValue after all.
  384. if (pszValue[0] &&
  385. (pszValueA = (LPSTR)GetEntirePrivateProfileStringAorW(
  386. strSectionA, strKey,
  387. strFileName, FALSE)) != NULL &&
  388. lstrcmpA(pszValue, pszValueA) == 0) {
  389. // our shadow is still good - run with it
  390. // Change [Section.A] to [Section.W]
  391. strSectionA[lstrlenA(strSectionA) - 1] = 'W';
  392. pszUTF7 = (LPSTR)GetEntirePrivateProfileStringAorW(
  393. strSectionA, strKey,
  394. strFileName, FALSE);
  395. dwRc = 0; // Assume something goes wrong
  396. if (pszUTF7) {
  397. dwRc = SHAnsiToUnicodeCP(CP_UTF7, pszUTF7, pwszReturnedString, cchSize);
  398. }
  399. if (dwRc == 0) {
  400. // Problem converting to UTF7 - just use the ANSI version
  401. dwRc = SHAnsiToUnicode(pszValue, pwszReturnedString, cchSize);
  402. }
  403. } else {
  404. // String was empty or couldn't get [Section.A] shadow or
  405. // shadow doesn't match. Just use the regular string.
  406. dwRc = SHAnsiToUnicode(pszValue, pwszReturnedString, cchSize);
  407. }
  408. // The SHXxxToYyy functions include the terminating zero,
  409. // which we want to exclude.
  410. if (dwRc > 0)
  411. dwRc--;
  412. } else {
  413. // Fatal error reading values from file; just use the boring API
  414. CStrOut strOut(pwszReturnedString, cchSize);
  415. dwRc = GetPrivateProfileStringA(strSection,
  416. strKey,
  417. "",
  418. strOut, cchSize,
  419. strFileName);
  420. strOut.ConvertIncludingNul();
  421. }
  422. if (pszValue)
  423. LocalFree(pszValue);
  424. if (pszValueA)
  425. LocalFree(pszValueA);
  426. if (pszUTF7)
  427. LocalFree(pszUTF7);
  428. return dwRc;
  429. }
  430. DWORD WINAPI SHGetIniStringW(LPCWSTR pwszSection, LPCWSTR pwszKey, LPWSTR pwszReturnedString, DWORD cchSize, LPCWSTR pwszFileName)
  431. {
  432. VALIDATE_OUTBUF(pwszReturnedString, cchSize);
  433. // We have no way of encoding these two, so they had better by 7-bit clean
  434. // We also do not support "get all section names" or "get entire section".
  435. AssertMsg(pwszSection != NULL,
  436. TEXT("SHGetIniStringW: Section name cannot be NULL; bug in caller"));
  437. AssertMsg(Is7BitClean(pwszSection),
  438. TEXT("SHGetIniStringW: Section name not 7-bit clean; bug in caller"));
  439. AssertMsg(pwszKey != NULL,
  440. TEXT("SHGetIniStringW: Key name cannot be NULL; bug in caller"));
  441. AssertMsg(Is7BitClean(pwszKey),
  442. TEXT("SHGetIniStringW: Key name not 7-bit clean; bug in caller"));
  443. CStrSectionX strSectionX(pwszSection);
  444. if (g_bRunningOnNT)
  445. return GetPrivateProfileStringMultiW(pwszSection, pwszKey,
  446. strSectionX,
  447. pwszReturnedString, cchSize,
  448. pwszFileName);
  449. else
  450. return GetPrivateProfileStringMultiA(pwszSection, pwszKey,
  451. strSectionX,
  452. pwszReturnedString, cchSize,
  453. pwszFileName);
  454. }
  455. //+---------------------------------------------------------------------------
  456. //
  457. // CreateURLFileContents
  458. //
  459. // shdocvw.dll and url.dll need to create in-memory images
  460. // of URL files, so this helper function does all the grunky work so they
  461. // can remain insulated from the way we encode Unicode strings into INI files.
  462. // The resulting memory should be freed via GlobalFree().
  463. //
  464. // Writes a string into the URL file. If fWrite is FALSE, then
  465. // then just do the math and don't actually write anything. This lets us
  466. // use one function to handle both the measurement pass and the rendering
  467. // pass.
  468. //
  469. LPSTR AddToURLFileContents(LPSTR pszFile, LPCSTR psz, BOOL fWrite)
  470. {
  471. int cch = lstrlenA(psz);
  472. if (fWrite) {
  473. memcpy(pszFile, psz, cch);
  474. }
  475. pszFile += cch;
  476. return pszFile;
  477. }
  478. LPSTR AddURLFileSection(LPSTR pszFile, LPCSTR pszSuffix, LPCSTR pszUrl, BOOL fWrite)
  479. {
  480. pszFile = AddToURLFileContents(pszFile, "[InternetShortcut", fWrite);
  481. pszFile = AddToURLFileContents(pszFile, pszSuffix, fWrite);
  482. pszFile = AddToURLFileContents(pszFile, "]\r\nURL=", fWrite);
  483. pszFile = AddToURLFileContents(pszFile, pszUrl, fWrite);
  484. pszFile = AddToURLFileContents(pszFile, "\r\n", fWrite);
  485. return pszFile;
  486. }
  487. //
  488. // The file consists of an [InternetShortcut] section, followed if
  489. // necessary by [InternetShortcut.A] and [InternetShortcut.W].
  490. //
  491. LPSTR AddURLFileContents(LPSTR pszFile, LPCSTR pszUrl, LPCSTR pszUTF7, BOOL fWrite)
  492. {
  493. pszFile = AddURLFileSection(pszFile, "", pszUrl, fWrite);
  494. if (pszUTF7) {
  495. pszFile = AddURLFileSection(pszFile, ".A", pszUrl, fWrite);
  496. pszFile = AddURLFileSection(pszFile, ".W", pszUTF7, fWrite);
  497. }
  498. return pszFile;
  499. }
  500. //
  501. // Returns number of bytes in file contents (not including trailing NULL),
  502. // or an OLE error code. If ppszOut is NULL, then no contents are returned.
  503. //
  504. HRESULT GenerateURLFileContents(LPCWSTR pwszUrl, LPCSTR pszUrl, LPSTR *ppszOut)
  505. {
  506. HRESULT hr = 0;
  507. if (ppszOut)
  508. *ppszOut = NULL;
  509. if (pwszUrl && pszUrl) {
  510. CStrUTF7 strUTF7; // Assume no UTF7 needed.
  511. if (!Is7BitClean(pwszUrl)) {
  512. //
  513. // The value is not 7-bit clean. Must create a UTF7 version.
  514. //
  515. strUTF7.SetUnicode(pwszUrl);
  516. }
  517. hr = PtrToUlong(AddURLFileContents(NULL, pszUrl, strUTF7, FALSE));
  518. ASSERT(SUCCEEDED(hr));
  519. if (ppszOut) {
  520. *ppszOut = (LPSTR)GlobalAlloc(GMEM_FIXED, hr + 1);
  521. if (*ppszOut) {
  522. LPSTR pszEnd = AddURLFileContents(*ppszOut, pszUrl, strUTF7, TRUE);
  523. *pszEnd = '\0';
  524. } else {
  525. hr = E_OUTOFMEMORY;
  526. }
  527. }
  528. }
  529. //
  530. // Double-check the value we return.
  531. //
  532. if (SUCCEEDED(hr) && ppszOut) {
  533. ASSERT(hr == lstrlenA(*ppszOut));
  534. }
  535. return hr;
  536. }
  537. HRESULT CreateURLFileContentsW(LPCWSTR pwszUrl, LPSTR *ppszOut)
  538. {
  539. CStrIn strUrl(pwszUrl);
  540. return GenerateURLFileContents(pwszUrl, strUrl, ppszOut);
  541. }
  542. HRESULT CreateURLFileContentsA(LPCSTR pszUrl, LPSTR *ppszOut)
  543. {
  544. CStrInW strUrl(pszUrl);
  545. return GenerateURLFileContents(strUrl, pszUrl, ppszOut);
  546. }
  547. DWORD SHGetIniStringUTF7W(LPCWSTR lpSection, LPCWSTR lpKey, LPWSTR lpBuf, DWORD nSize, LPCWSTR lpFile)
  548. {
  549. if (*lpKey == CH_CANBEUNICODEW)
  550. return SHGetIniStringW(lpSection, lpKey+1, lpBuf, nSize, lpFile);
  551. else
  552. return GetPrivateProfileStringWrapW(lpSection, lpKey, L"", lpBuf, nSize, lpFile);
  553. }
  554. BOOL SHSetIniStringUTF7W(LPCWSTR lpSection, LPCWSTR lpKey, LPCWSTR lpString, LPCWSTR lpFile)
  555. {
  556. if (*lpKey == CH_CANBEUNICODEW)
  557. return SHSetIniStringW(lpSection, lpKey+1, lpString, lpFile);
  558. else
  559. return WritePrivateProfileStringWrapW(lpSection, lpKey, lpString, lpFile);
  560. }