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.

338 lines
11 KiB

  1. #include "priv.h"
  2. #include "unicwrap.h"
  3. /*****************************************************************************\
  4. FUNCTION: SHLoadRegUIString
  5. DESCRIPTION:
  6. loads the data from the value given the hkey and
  7. pszValue. if the data is of the form:
  8. @[path\]<dllname>,-<strId>
  9. the string with id <strId> from <dllname> will be
  10. loaded. if not explicit path is provided then the
  11. dll will be chosen according to pluggable UI
  12. specifications, if possible.
  13. if the value's data doesn't yield a successful
  14. string load, then the data itself is returned
  15. NOTE:
  16. These strings are always loaded with cross codepage support.
  17. WARNING:
  18. This function can end up calling LoadLibrary and FreeLibrary.
  19. Therefore, you must not call SHLoadRegUIString during process
  20. attach or process detach.
  21. PARAMETERS:
  22. hkey - hkey of where to look for pszValue
  23. pszValue - value with text string or indirector (see above) to use
  24. pszOutBuf - buffer in which to return the data or indirected string
  25. cchOutBuf - size of pszOutBuf
  26. \*****************************************************************************/
  27. LANGID GetNormalizedLangId(DWORD dwFlag);
  28. STDAPI
  29. SHLoadRegUIStringW(HKEY hkey,
  30. LPCWSTR pszValue,
  31. LPWSTR pszOutBuf,
  32. UINT cchOutBuf)
  33. {
  34. HRESULT hr;
  35. RIP(hkey != NULL);
  36. RIP(hkey != INVALID_HANDLE_VALUE);
  37. RIP(NULL == pszValue || IS_VALID_STRING_PTRW(pszValue, -1));
  38. RIP(IS_VALID_WRITE_BUFFER(pszOutBuf, WCHAR, cchOutBuf));
  39. DEBUGWhackPathBufferW(pszOutBuf, cchOutBuf);
  40. // Lots of people (regfldr.cpp, for example)
  41. // assume they'll get back an empty string on failure,
  42. // so let's give the public what it wants
  43. if (cchOutBuf)
  44. pszOutBuf[0] = 0;
  45. hr = E_INVALIDARG;
  46. if (hkey != INVALID_HANDLE_VALUE &&
  47. hkey != NULL &&
  48. pszOutBuf != NULL)
  49. {
  50. DWORD cb;
  51. DWORD dwRet;
  52. WCHAR * pszValueDataBuf;
  53. hr = E_FAIL;
  54. // first try to get the indirected text which will
  55. // point to a string id in a dll somewhere... this
  56. // allows plugUI enabled registry UI strings
  57. pszValueDataBuf = pszOutBuf;
  58. cb = CbFromCchW(cchOutBuf);
  59. dwRet = SHQueryValueExW(hkey, pszValue, NULL, NULL, (LPBYTE)pszValueDataBuf, &cb);
  60. if (dwRet == ERROR_SUCCESS || dwRet == ERROR_MORE_DATA)
  61. {
  62. BOOL fAlloc;
  63. fAlloc = (dwRet == ERROR_MORE_DATA);
  64. // if we didn't have space, this is where we correct the problem.
  65. // we create a buffer big enough, load the data, and leave
  66. // ourselves with pszValueDataBuf pointing at a valid buffer
  67. // containing valid data, exactly what we hoped for in the
  68. // SHQueryValueExW above
  69. if (fAlloc)
  70. {
  71. pszValueDataBuf = new WCHAR[(cb+1)/2];
  72. if (pszValueDataBuf != NULL)
  73. {
  74. // try to load again... overwriting dwRet on purpose
  75. // because we only need to know whether we successfully filled
  76. // the buffer at some point (whether then or now)
  77. dwRet = SHQueryValueExW(hkey, pszValue, NULL, NULL, (LPBYTE)pszValueDataBuf, &cb);
  78. }
  79. else
  80. {
  81. hr = E_OUTOFMEMORY;
  82. }
  83. }
  84. // proceed if we succesfully loaded something via one of the
  85. // two SHQueryValueExW calls.
  86. // we should have the data we want in a buffer pointed
  87. // to by pszValueDataBuf.
  88. if (dwRet == ERROR_SUCCESS)
  89. {
  90. hr = SHLoadIndirectString(pszValueDataBuf, pszOutBuf, cchOutBuf, NULL);
  91. }
  92. if (fAlloc && pszValueDataBuf != NULL)
  93. {
  94. delete [] pszValueDataBuf;
  95. }
  96. }
  97. }
  98. return hr;
  99. }
  100. STDAPI
  101. SHLoadRegUIStringA(HKEY hkey,
  102. LPCSTR pszValue,
  103. LPSTR pszOutBuf,
  104. UINT cchOutBuf)
  105. {
  106. HRESULT hr;
  107. RIP(hkey != NULL);
  108. RIP(hkey != INVALID_HANDLE_VALUE);
  109. RIP(IS_VALID_STRING_PTRA(pszValue, -1));
  110. RIP(IS_VALID_WRITE_BUFFER(pszOutBuf, char, cchOutBuf));
  111. CStrInW strV(pszValue);
  112. CStrOutW strOut(pszOutBuf, cchOutBuf);
  113. hr = SHLoadRegUIStringW(hkey, strV, strOut, strOut.BufSize());
  114. return hr;
  115. }
  116. HRESULT _LoadDllString(LPCWSTR pszSource, LPWSTR pszOutBuf, UINT cchOutBuf)
  117. {
  118. HRESULT hr = E_FAIL;
  119. WCHAR * szParseBuf;
  120. int nStrId;
  121. UINT cchSource = lstrlenW(pszSource)+1;
  122. szParseBuf = new WCHAR[cchSource];
  123. if (szParseBuf != NULL)
  124. {
  125. StrCpyW(szParseBuf, pszSource);
  126. // see if this is a special string reference.
  127. // such strings take the form [path\]dllname.dll,-123
  128. // where 123 is the id of the string resource
  129. // note that reference by index is not permitted
  130. nStrId = PathParseIconLocationW(szParseBuf);
  131. nStrId *= -1;
  132. if (nStrId > 0)
  133. {
  134. LPWSTR pszDllName;
  135. HINSTANCE hinst;
  136. BOOL fUsedMLLoadLibrary = FALSE;
  137. pszDllName = PathFindFileNameW(szParseBuf);
  138. ASSERT(pszDllName >= szParseBuf);
  139. // try loading the dll with MLLoadLibrary, but
  140. // only if an explicit path was not provided.
  141. // we assume an explicit path means that
  142. // the caller knows precisely which dll is needed
  143. // use MLLoadLibrary first, otherwise we'll miss
  144. // out chance to have plugUI behavior
  145. hinst = NULL;
  146. if (pszDllName == szParseBuf)
  147. {
  148. if (StrStrI(pszDllName, L"LC.DLL"))
  149. {
  150. // note: using HINST_THISDLL (below) is sort of a hack because that's
  151. // techinically supposed to be the *parent* dll's hinstance...
  152. // however we get called from lots of places and therefore
  153. // don't know the parent dll, and the hinst for browseui.dll
  154. // is good enough since all the hinst is really used for is to
  155. // find the path to check if the install language is the
  156. // currently selected UI language. this will usually be
  157. // something like "\winnt\system32"
  158. hinst = MLLoadLibraryW(pszDllName, HINST_THISDLL, ML_CROSSCODEPAGE);
  159. fUsedMLLoadLibrary = (hinst != NULL);
  160. }
  161. else
  162. hinst = LoadLibraryExWrapW(pszDllName, NULL, LOAD_LIBRARY_AS_DATAFILE);
  163. }
  164. if (!hinst)
  165. {
  166. // our last chance to load something is if a full
  167. // path was provided... if there's a full path it
  168. // will start at the beginning of the szParseBuf buffer
  169. if (pszDllName > szParseBuf)
  170. {
  171. // don't bother if the file isn't there
  172. // failling in LoadLibrary is slow
  173. if (PathFileExistsW(szParseBuf))
  174. {
  175. hinst = LoadLibraryExWrapW(szParseBuf, NULL, LOAD_LIBRARY_AS_DATAFILE);
  176. }
  177. }
  178. }
  179. if (hinst)
  180. {
  181. // dll found, so load the string
  182. if (LoadStringWrapW(hinst, nStrId, pszOutBuf, cchOutBuf))
  183. {
  184. hr = S_OK;
  185. }
  186. else
  187. {
  188. TraceMsg(TF_WARNING,
  189. "SHLoadRegUIString(): Failure loading string %d from module %ws for valid load request %ws.",
  190. nStrId,
  191. szParseBuf,
  192. pszSource);
  193. }
  194. if (fUsedMLLoadLibrary)
  195. {
  196. MLFreeLibrary(hinst);
  197. }
  198. else
  199. {
  200. FreeLibrary(hinst);
  201. }
  202. }
  203. }
  204. delete [] szParseBuf;
  205. }
  206. else
  207. {
  208. hr = E_OUTOFMEMORY;
  209. }
  210. return hr;
  211. }
  212. inline BOOL _CanCacheMUI()
  213. {
  214. if (!g_bRunningOnNT)
  215. return (GetNormalizedLangId(ML_CROSSCODEPAGE_NT) == MLGetUILanguage());
  216. return TRUE;
  217. }
  218. // Note: pszSource and pszOutBuf may be the same buffer
  219. LWSTDAPI SHLoadIndirectString(LPCWSTR pszSource, LPWSTR pszOutBuf, UINT cchOutBuf, void **ppvReserved)
  220. {
  221. HRESULT hr = E_FAIL;
  222. RIP(IS_VALID_WRITE_BUFFER(pszOutBuf, WCHAR, cchOutBuf));
  223. RIP(!ppvReserved);
  224. if (pszSource[0] == L'@') // "@dllname,-id" or "@dllname,-id?lid,string"
  225. {
  226. LPWSTR pszResource = StrDupW(pszSource);
  227. if (pszResource)
  228. {
  229. LANGID lidUI =0;
  230. // the LidString is there to support our old caching model.
  231. // the new caching model doesnt require any work for the caller
  232. LPWSTR pszLidString = StrChrW(pszResource+1, L'?');
  233. DWORD cchResource = lstrlen(pszResource);
  234. // used to use '@' as the second delimiter as well.
  235. // but it has collisions with filesystem paths.
  236. if (!pszLidString)
  237. pszLidString = StrChrW(pszResource+1, L'@');
  238. if (pszLidString)
  239. {
  240. cchResource = (DWORD)(pszLidString - pszResource);
  241. // NULL terminate the dll,id just in case we need to actually load
  242. pszResource[cchResource] = 0;
  243. }
  244. DWORD cb = CbFromCchW(cchOutBuf);
  245. hr = SKGetValue(SHELLKEY_HKCULM_MUICACHE, NULL, pszResource, NULL, pszOutBuf, &cb);
  246. if (FAILED(hr))
  247. {
  248. WCHAR wszDllId[MAX_PATH + 1 + 6]; // path + comma + -65536
  249. SHExpandEnvironmentStringsW(pszResource+1, wszDllId, ARRAYSIZE(wszDllId));
  250. hr = _LoadDllString(wszDllId, pszOutBuf, cchOutBuf);
  251. // Might as well write the new string out so we don't have to load the DLL next time through
  252. // but we don't write cross codepage string on Win9x
  253. if (SUCCEEDED(hr) && _CanCacheMUI())
  254. {
  255. SKSetValue(SHELLKEY_HKCULM_MUICACHE, NULL, pszResource, REG_SZ, pszOutBuf, CbFromCchW(lstrlenW(pszOutBuf)+1));
  256. }
  257. }
  258. LocalFree(pszResource);
  259. }
  260. else
  261. hr = E_OUTOFMEMORY;
  262. if (FAILED(hr))
  263. {
  264. if (cchOutBuf)
  265. pszOutBuf[0] = L'\0'; // can't hand out an "@shell32.dll,-525" string
  266. }
  267. }
  268. else
  269. {
  270. if (pszOutBuf != pszSource)
  271. StrCpyN(pszOutBuf, pszSource, cchOutBuf);
  272. hr = S_OK;
  273. }
  274. return hr;
  275. }