inistr.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 22k
Category:

Windows Kernel

Development Platform:

Visual C++

  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 screw 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] = '';
  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 (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 ? (LPTSTR)strValue : (LPTSTR)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, "]rnURL=", fWrite);
  483.     pszFile = AddToURLFileContents(pszFile, pszUrl, fWrite);
  484.     pszFile = AddToURLFileContents(pszFile, "rn", 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 = '';
  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. }