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.

359 lines
9.6 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. CStringPI.cpp
  5. Abstract:
  6. Win32 API wrappers for CString
  7. Created:
  8. 02/27/2001 robkenny Created
  9. 08/14/2001 robkenny Moved code inside the ShimLib namespace.
  10. --*/
  11. #include <nt.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include <windows.h>
  15. #include "ShimLib.h"
  16. #include "Shlobj.h"
  17. #include "StrSafe.h"
  18. namespace ShimLib
  19. {
  20. /*====================================================================================*/
  21. /*++
  22. Read a registry value into this CString.
  23. REG_EXPAND_SZ is automatically expanded and the type is changed to REG_SZ
  24. If the type is not REG_SZ or REG_EXPAND_SZ then csValue is unmodified
  25. and *lpType returns the value of the key.
  26. --*/
  27. LONG RegQueryValueExW(
  28. CString & csValue,
  29. HKEY hKeyRoot,
  30. const WCHAR * lpszKey,
  31. const WCHAR * lpszValue)
  32. {
  33. HKEY hKey;
  34. LONG success = RegOpenKeyExW(hKeyRoot, lpszKey, 0, KEY_READ, &hKey);
  35. if (success == ERROR_SUCCESS)
  36. {
  37. DWORD ccbValueSize = 0;
  38. DWORD dwType;
  39. success = ::RegQueryValueExW(hKey, lpszValue, 0, &dwType, NULL, &ccbValueSize);
  40. if (success == ERROR_SUCCESS)
  41. {
  42. if (dwType == REG_SZ || dwType == REG_EXPAND_SZ)
  43. {
  44. // MSDN says: Buffer might not be null terminated, so be very careful
  45. //
  46. // Of course, RegQueryValueEx does have a hack that takes care of the EOS,
  47. // but only if the buffer is large enough, which of course it never is
  48. // when you query for the size of the buffer!
  49. //
  50. // So, the moral of the story is don't trust RegQueryValueEx
  51. // to properly terminate the string.
  52. // cchBuffer is the number of characters, rounded up for paranoia
  53. // Can we ever get an odd number of bytes for a REG_SZ?
  54. DWORD cchBuffer = (ccbValueSize + 1) / sizeof(WCHAR);
  55. WCHAR * lpszBuffer = NULL;
  56. CSTRING_TRY
  57. {
  58. // Grab an extra character in case the reg value doesn't have EOS
  59. // We are being extra cautious
  60. lpszBuffer = csValue.GetBuffer(cchBuffer + 1);
  61. // Recalculate ccbValueSize based on the number of chars we just allocated.
  62. // Notice that this size is 1 WCHAR smaller than we just asked for, this
  63. // is so we'll always have room for 1 more character after the next call
  64. // to RegQueryValueExW
  65. ccbValueSize = cchBuffer * sizeof(WCHAR);
  66. }
  67. CSTRING_CATCH
  68. {
  69. // Close the registry key and pass the exception onto the caller.
  70. ::RegCloseKey(hKey);
  71. CSTRING_THROW_EXCEPTION
  72. }
  73. success = ::RegQueryValueExW(hKey, lpszValue, 0, &dwType, (BYTE*)lpszBuffer, &ccbValueSize);
  74. if (success == ERROR_SUCCESS)
  75. {
  76. // Make sure the registy value is still of the proper type
  77. // It could have been modified by another process or thread....
  78. if (dwType == REG_SZ || dwType == REG_EXPAND_SZ)
  79. {
  80. // Convert the data byte size into number of chars;
  81. // if ccbValueSize is odd we'll ignore the last byte.
  82. DWORD cchValueSize = ccbValueSize / sizeof(WCHAR);
  83. // cchValueSize might count the EOS character,
  84. // (ReleaseBuffer expects the string length)
  85. if (cchValueSize > 0 && lpszBuffer[cchValueSize-1] == 0)
  86. {
  87. cchValueSize -= 1;
  88. // cchValueSize now contains the string length.
  89. }
  90. // ReleaseBuffer ensures the string is properly terminated.
  91. csValue.ReleaseBuffer(cchValueSize);
  92. if (dwType == REG_EXPAND_SZ)
  93. {
  94. CSTRING_TRY
  95. {
  96. csValue.ExpandEnvironmentStringsW();
  97. }
  98. CSTRING_CATCH
  99. {
  100. // Error expanding the environment string
  101. success = ERROR_NOT_ENOUGH_MEMORY;
  102. }
  103. }
  104. }
  105. }
  106. if (success != ERROR_SUCCESS)
  107. {
  108. csValue.ReleaseBuffer(0);
  109. }
  110. }
  111. else
  112. {
  113. // Key is of the wrong type, return an error
  114. success = ERROR_INVALID_PARAMETER;
  115. }
  116. }
  117. ::RegCloseKey(hKey);
  118. }
  119. if (success != ERROR_SUCCESS)
  120. {
  121. csValue.Truncate(0);
  122. }
  123. return success;
  124. }
  125. /*====================================================================================*/
  126. BOOL SHGetSpecialFolderPathW(
  127. CString & csFolder,
  128. int nFolder,
  129. HWND hwndOwner
  130. )
  131. {
  132. // Force the size to MAX_PATH because there is no way to determine necessary buffer size.
  133. WCHAR * lpsz = csFolder.GetBuffer(MAX_PATH);
  134. BOOL bSuccess = ::SHGetSpecialFolderPathW(hwndOwner, lpsz, nFolder, FALSE);
  135. csFolder.ReleaseBuffer(-1); // Don't know the length of the resulting string
  136. return bSuccess;
  137. }
  138. /*====================================================================================*/
  139. CStringToken::CStringToken(const CString & csToken, const CString & csDelimit)
  140. {
  141. m_nPos = 0;
  142. m_csToken = csToken;
  143. m_csDelimit = csDelimit;
  144. }
  145. /*++
  146. Grab the next token
  147. --*/
  148. BOOL CStringToken::GetToken(CString & csNextToken, int & nPos) const
  149. {
  150. // Already reached the end of the string
  151. if (nPos > m_csToken.GetLength())
  152. {
  153. csNextToken.Truncate(0);
  154. return FALSE;
  155. }
  156. int nNextToken;
  157. // Skip past all the leading seperators
  158. nPos = m_csToken.FindOneNotOf(m_csDelimit, nPos);
  159. if (nPos < 0)
  160. {
  161. // Nothing but seperators
  162. csNextToken.Truncate(0);
  163. nPos = m_csToken.GetLength() + 1;
  164. return FALSE;
  165. }
  166. // Find the next seperator
  167. nNextToken = m_csToken.FindOneOf(m_csDelimit, nPos);
  168. if (nNextToken < 0)
  169. {
  170. // Did not find a seperator, return remaining string
  171. m_csToken.Mid(nPos, csNextToken);
  172. nPos = m_csToken.GetLength() + 1;
  173. return TRUE;
  174. }
  175. // Found a seperator, return the string
  176. m_csToken.Mid(nPos, nNextToken - nPos, csNextToken);
  177. nPos = nNextToken;
  178. return TRUE;
  179. }
  180. /*++
  181. Grab the next token
  182. --*/
  183. BOOL CStringToken::GetToken(CString & csNextToken)
  184. {
  185. return GetToken(csNextToken, m_nPos);
  186. }
  187. /*++
  188. Count the number of remaining tokens.
  189. --*/
  190. int CStringToken::GetCount() const
  191. {
  192. int nTokenCount = 0;
  193. int nNextToken = m_nPos;
  194. CString csTok;
  195. while (GetToken(csTok, nNextToken))
  196. {
  197. nTokenCount += 1;
  198. }
  199. return nTokenCount;
  200. }
  201. /*====================================================================================*/
  202. /*====================================================================================*/
  203. /*++
  204. A simple class to assist in command line parsing
  205. --*/
  206. CStringParser::CStringParser(const WCHAR * lpszCl, const WCHAR * lpszSeperators)
  207. {
  208. m_ncsArgList = 0;
  209. m_csArgList = NULL;
  210. if (!lpszCl || !*lpszCl)
  211. {
  212. return; // no command line == no tokens
  213. }
  214. CString csCl(lpszCl);
  215. CString csSeperator(lpszSeperators);
  216. if (csSeperator.Find(L' ', 0) >= 0)
  217. {
  218. // Special processing for blank seperated cl
  219. SplitWhite(csCl);
  220. }
  221. else
  222. {
  223. SplitSeperator(csCl, csSeperator);
  224. }
  225. }
  226. CStringParser::~CStringParser()
  227. {
  228. if (m_csArgList)
  229. {
  230. delete [] m_csArgList;
  231. }
  232. }
  233. /*++
  234. Split up the command line based on the seperators
  235. --*/
  236. void CStringParser::SplitSeperator(const CString & csCl, const CString & csSeperator)
  237. {
  238. CStringToken csParser(csCl, csSeperator);
  239. CString csTok;
  240. m_ncsArgList = csParser.GetCount();
  241. m_csArgList = new CString[m_ncsArgList];
  242. if (!m_csArgList)
  243. {
  244. CSTRING_THROW_EXCEPTION
  245. }
  246. // Break the command line into seperate tokens
  247. for (int i = 0; i < m_ncsArgList; ++i)
  248. {
  249. csParser.GetToken(m_csArgList[i]);
  250. }
  251. }
  252. /*++
  253. Split up the command line based on whitespace,
  254. this works exactly like the CMD's command line.
  255. --*/
  256. void CStringParser::SplitWhite(const CString & csCl)
  257. {
  258. LPWSTR * argv = _CommandLineToArgvW(csCl, &m_ncsArgList);
  259. if (!argv)
  260. {
  261. CSTRING_THROW_EXCEPTION
  262. }
  263. m_csArgList = new CString[m_ncsArgList];
  264. if (!m_csArgList)
  265. {
  266. CSTRING_THROW_EXCEPTION
  267. }
  268. for (int i = 0; i < m_ncsArgList; ++i)
  269. {
  270. m_csArgList[i] = argv[i];
  271. }
  272. LocalFree(argv);
  273. }
  274. /*====================================================================================*/
  275. /*====================================================================================*/
  276. }; // end of namespace ShimLib