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.

811 lines
20 KiB

  1. //+---------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1993 - 1997.
  5. //
  6. // File: datapkt.cpp
  7. //
  8. // Contents: Implements the class CDataPacket to manages diverse data
  9. // packets needing to be written to various databases
  10. //
  11. // Classes:
  12. //
  13. // Methods: CDataPacket::CDataPacket (x 7)
  14. // CDataPacket::~CDataPacket
  15. // CDataPacket::ChgSzValue
  16. // CDataPacket::ChgDwordValue
  17. // CDataPacket::ChgACL
  18. // CDataPacket::ChgPassword
  19. // CDataPacket::ChgSrvIdentity
  20. //
  21. // History: 23-Apr-96 BruceMa Created.
  22. // 12-Dec-96 RonanS Added copy constructor to CDataPacket
  23. // to get around bugs when copying CDataPacket.
  24. // Fixed memory leaks in destructor.
  25. // Simplified constructor code.
  26. // 09-Jan-97 SteveBl Modified to support IAccessControl.
  27. //
  28. //----------------------------------------------------------------------
  29. #include "stdafx.h"
  30. #include "assert.h"
  31. #include "datapkt.h"
  32. #if !defined(STANDALONE_BUILD)
  33. extern "C"
  34. {
  35. #include <getuser.h>
  36. }
  37. #endif
  38. #include "util.h"
  39. static TCHAR * TStrDup(const TCHAR *lpszString)
  40. {
  41. TCHAR * lpszTmp = NULL;
  42. int nStrlen = 0;
  43. if (lpszString )
  44. {
  45. lpszTmp = new TCHAR[_tcslen(lpszString) + 1];
  46. ASSERT(lpszTmp);
  47. _tcscpy(lpszTmp, lpszString);
  48. }
  49. return lpszTmp;
  50. }
  51. CDataPacket::CDataPacket(void)
  52. {
  53. m_tagType = Empty;
  54. m_fDelete = FALSE;
  55. m_fDeleteHive = FALSE;
  56. m_hRoot = 0;
  57. SetModified(TRUE);
  58. }
  59. CDataPacket::CDataPacket(HKEY hRoot,
  60. TCHAR *szKeyPath,
  61. TCHAR *szValueName,
  62. DWORD dwValue)
  63. :m_szKeyPath(szKeyPath), m_szValueName(szValueName)
  64. {
  65. m_tagType = NamedValueDword;
  66. m_hRoot = hRoot;
  67. pkt.nvdw.dwValue = dwValue;
  68. SetModified(TRUE);
  69. m_fDelete = FALSE;
  70. m_fDeleteHive = FALSE;
  71. }
  72. CDataPacket::CDataPacket(HKEY hRoot,
  73. TCHAR *szKeyPath,
  74. TCHAR *szValueName,
  75. SECURITY_DESCRIPTOR *pSec,
  76. BOOL fSelfRelative)
  77. :m_szKeyPath(szKeyPath), m_szValueName(szValueName)
  78. {
  79. int err;
  80. ULONG cbLen;
  81. SECURITY_DESCRIPTOR *pSD;
  82. m_tagType = SingleACL;
  83. m_hRoot = hRoot;
  84. // Get the security descriptor into self relative form so we
  85. // can cache it
  86. // Force first call to fail so we can get the real size needed
  87. if (!fSelfRelative)
  88. {
  89. cbLen = 1;
  90. if (!MakeSelfRelativeSD(pSec, NULL, &cbLen))
  91. {
  92. err = GetLastError();
  93. }
  94. // Now really do it
  95. pSD = (SECURITY_DESCRIPTOR *) GlobalAlloc(GMEM_FIXED, cbLen);
  96. if (!pSD)
  97. {
  98. ReportOutOfMemAndTerminate();
  99. // will never get here
  100. return;
  101. }
  102. if (!MakeSelfRelativeSD(pSec, pSD, &cbLen))
  103. {
  104. err = GetLastError();
  105. }
  106. pkt.acl.pSec = pSD;
  107. }
  108. else
  109. {
  110. // The security descriptor is aready in self relative form
  111. // as it was read directly from the registry. However, we still
  112. // have to copy the it.
  113. g_util.CopySD(pSec, &pkt.acl.pSec);
  114. }
  115. SetModified(TRUE);
  116. m_fDelete = FALSE;
  117. m_fDeleteHive = FALSE;
  118. }
  119. CDataPacket::CDataPacket(HKEY hKey,
  120. HKEY *phClsids,
  121. unsigned cClsids,
  122. TCHAR *szTitle,
  123. SECURITY_DESCRIPTOR *pSecOrig,
  124. SECURITY_DESCRIPTOR *pSec,
  125. BOOL fSelfRelative)
  126. {
  127. ULONG cbLen;
  128. SECURITY_DESCRIPTOR *pSD;
  129. m_tagType = RegKeyACL;
  130. m_hRoot = hKey;
  131. pkt.racl.phClsids = phClsids;
  132. pkt.racl.cClsids = cClsids;
  133. pkt.racl.szTitle = TStrDup(szTitle);
  134. // Get the new security descriptor into self relative form so we
  135. // can cache it (if we have to)
  136. if (!fSelfRelative)
  137. {
  138. // Force first call to fail so we can get the real size needed
  139. cbLen = 1;
  140. MakeSelfRelativeSD(pSec, NULL, &cbLen);
  141. // Now really do it
  142. pSD = (SECURITY_DESCRIPTOR *) GlobalAlloc(GMEM_FIXED, cbLen);
  143. if (!pSD)
  144. {
  145. ReportOutOfMemAndTerminate();
  146. // will never get here
  147. return;
  148. }
  149. MakeSelfRelativeSD(pSec, pSD, &cbLen);
  150. pkt.racl.pSec = pSD;
  151. }
  152. else
  153. {
  154. g_util.CopySD(pSec, &pkt.racl.pSec);
  155. }
  156. // The original security descriptor is aready in self relative form
  157. // as it was read directly from the registry. (The edited SD from the
  158. // ACL editor is in absolute form.) However, we still have to copy the
  159. // original SD.
  160. g_util.CopySD(pSecOrig, &pkt.racl.pSecOrig);
  161. SetModified(TRUE);
  162. m_fDelete = FALSE;
  163. m_fDeleteHive = FALSE;
  164. }
  165. CDataPacket::CDataPacket(TCHAR *szPassword,
  166. CLSID appid)
  167. {
  168. m_tagType = Password;
  169. pkt.pw.szPassword = TStrDup(szPassword);
  170. pkt.pw.appid = appid;
  171. SetModified(TRUE);
  172. m_fDelete = FALSE;
  173. m_hRoot = 0;
  174. m_fDeleteHive = FALSE;
  175. }
  176. CDataPacket::CDataPacket(TCHAR *szServiceName,
  177. TCHAR *szIdentity)
  178. {
  179. m_hRoot = 0;
  180. m_tagType = ServiceIdentity;
  181. pkt.si.szServiceName = TStrDup(szServiceName);
  182. pkt.si.szIdentity = TStrDup(szIdentity);
  183. SetModified(TRUE);
  184. m_fDelete = FALSE;
  185. m_fDeleteHive = FALSE;
  186. }
  187. CRegSzNamedValueDp::CRegSzNamedValueDp(const CRegSzNamedValueDp& rDataPacket)
  188. : CDataPacket((const CDataPacket & ) rDataPacket)
  189. {
  190. m_szValue = rDataPacket.m_szValue;
  191. }
  192. CDataPacket::CDataPacket( const CDataPacket & rDataPacket)
  193. :m_szKeyPath (rDataPacket.m_szKeyPath), m_szValueName(rDataPacket.m_szValueName)
  194. {
  195. m_tagType = rDataPacket.m_tagType;
  196. m_fModified = rDataPacket.m_fModified;
  197. m_fDelete = rDataPacket.m_fDelete;
  198. m_hRoot = rDataPacket.m_hRoot;
  199. switch (m_tagType)
  200. {
  201. case NamedValueSz:
  202. // handled by derived class
  203. break;
  204. case NamedValueDword:
  205. pkt.nvdw.dwValue = rDataPacket.pkt.nvdw.dwValue;
  206. break;
  207. case SingleACL:
  208. // Get the security descriptor into self relative form so we
  209. g_util.CopySD(rDataPacket.pkt.acl.pSec, &pkt.acl.pSec);
  210. break;
  211. case RegKeyACL:
  212. pkt.racl.phClsids = rDataPacket.pkt.racl.phClsids;
  213. pkt.racl.cClsids = rDataPacket.pkt.racl.cClsids;
  214. pkt.racl.szTitle = TStrDup(rDataPacket.pkt.racl.szTitle);
  215. g_util.CopySD(rDataPacket.pkt.racl.pSec, &pkt.racl.pSec);
  216. g_util.CopySD(rDataPacket.pkt.racl.pSecOrig, &pkt.racl.pSecOrig);
  217. break;
  218. case Password:
  219. pkt.pw.szPassword = TStrDup(rDataPacket.pkt.pw.szPassword);
  220. pkt.pw.appid = rDataPacket.pkt.pw.appid;
  221. break;
  222. case ServiceIdentity:
  223. pkt.si.szServiceName = TStrDup(rDataPacket.pkt.si.szServiceName);
  224. pkt.si.szIdentity = TStrDup(rDataPacket.pkt.si.szIdentity);
  225. break;
  226. case Empty:
  227. break;
  228. }
  229. }
  230. CDataPacket::~CDataPacket()
  231. {
  232. switch (m_tagType)
  233. {
  234. case NamedValueSz:
  235. // handled by derived class
  236. break;
  237. case NamedValueDword:
  238. break;
  239. case SingleACL:
  240. if (pkt.acl.pSec)
  241. GlobalFree(pkt.acl.pSec);
  242. break;
  243. case RegKeyACL:
  244. if (pkt.racl.szTitle)
  245. delete pkt.racl.szTitle;
  246. if (pkt.racl.pSec)
  247. GlobalFree(pkt.racl.pSec);
  248. if (pkt.racl.pSecOrig)
  249. GlobalFree(pkt.racl.pSecOrig);
  250. break;
  251. case Password:
  252. if (pkt.pw.szPassword)
  253. delete pkt.pw.szPassword;
  254. break;
  255. case ServiceIdentity:
  256. if (pkt.si.szServiceName)
  257. delete pkt.si.szServiceName;
  258. if (pkt.si.szIdentity)
  259. delete pkt.si.szIdentity;
  260. break;
  261. }
  262. }
  263. void CRegSzNamedValueDp::ChgSzValue(TCHAR *szValue)
  264. {
  265. assert(m_tagType == NamedValueSz);
  266. m_szValue = szValue;
  267. SetModified(TRUE);
  268. }
  269. void CDataPacket::ChgDwordValue(DWORD dwValue)
  270. {
  271. assert(m_tagType == NamedValueDword);
  272. pkt.nvdw.dwValue = dwValue;
  273. SetModified(TRUE);
  274. }
  275. void CDataPacket::ChgACL(SECURITY_DESCRIPTOR *pSec, BOOL fSelfRelative)
  276. {
  277. ULONG cbLen;
  278. SECURITY_DESCRIPTOR *pSD;
  279. assert(m_tagType == SingleACL || m_tagType == RegKeyACL);
  280. // Remove the previous security descriptor
  281. if (m_tagType == SingleACL)
  282. {
  283. GlobalFree(pkt.acl.pSec);
  284. pkt.acl.pSec = NULL;
  285. }
  286. else
  287. {
  288. GlobalFree(pkt.racl.pSec);
  289. pkt.racl.pSec = NULL;
  290. }
  291. // Put into self relative form (if necessary)
  292. if (!fSelfRelative)
  293. {
  294. cbLen = 1;
  295. MakeSelfRelativeSD(pSec, NULL, &cbLen);
  296. // Now really do it
  297. pSD = (SECURITY_DESCRIPTOR *) GlobalAlloc(GMEM_FIXED, cbLen);
  298. if (!pSD)
  299. {
  300. ReportOutOfMemAndTerminate();
  301. // will never get here
  302. return;
  303. }
  304. MakeSelfRelativeSD(pSec, pSD, &cbLen);
  305. // Store it
  306. if (m_tagType == SingleACL)
  307. {
  308. pkt.acl.pSec = pSD;
  309. }
  310. else
  311. {
  312. pkt.racl.pSec = pSD;
  313. }
  314. }
  315. else
  316. {
  317. if (m_tagType == SingleACL)
  318. {
  319. g_util.CopySD(pSec, &pkt.acl.pSec);
  320. }
  321. else
  322. {
  323. g_util.CopySD(pSec, &pkt.racl.pSec);
  324. }
  325. }
  326. SetModified(TRUE);
  327. }
  328. void CDataPacket::ChgPassword(TCHAR *szPassword)
  329. {
  330. if (m_tagType != Password)
  331. return;
  332. if (pkt.pw.szPassword)
  333. delete pkt.pw.szPassword;
  334. pkt.pw.szPassword = TStrDup(szPassword);
  335. SetModified(TRUE);
  336. }
  337. void CDataPacket::ChgSrvIdentity(TCHAR *szIdentity)
  338. {
  339. assert(m_tagType == ServiceIdentity);
  340. if (pkt.si.szIdentity)
  341. delete pkt.si.szIdentity;
  342. pkt.si.szIdentity = TStrDup(szIdentity);
  343. SetModified(TRUE);
  344. }
  345. void CDataPacket::MarkForDeletion(BOOL bDelete)
  346. {
  347. m_fDelete = bDelete;
  348. SetModified(TRUE);
  349. }
  350. void CDataPacket::MarkHiveForDeletion(BOOL bDelete)
  351. {
  352. m_fDelete = bDelete;
  353. m_fDeleteHive = bDelete;
  354. SetModified(TRUE);
  355. }
  356. int CDataPacket::Apply()
  357. {
  358. int err = ERROR_SUCCESS;
  359. if (m_fModified)
  360. {
  361. if (m_fDelete)
  362. err = Remove();
  363. else
  364. err = Update();
  365. }
  366. // Cleanup work
  367. if (err == ERROR_SUCCESS)
  368. {
  369. m_fModified = FALSE;
  370. }
  371. else
  372. {
  373. if (err == ERROR_ACCESS_DENIED)
  374. {
  375. g_util.CkForAccessDenied(ERROR_ACCESS_DENIED);
  376. }
  377. else
  378. {
  379. g_util.PostErrorMessage();
  380. }
  381. }
  382. return err;
  383. }
  384. int CDataPacket::Update()
  385. {
  386. int err = ERROR_SUCCESS;
  387. ASSERT(m_fModified);
  388. switch (m_tagType)
  389. {
  390. case Empty:
  391. break;
  392. case NamedValueSz:
  393. ASSERT(FALSE); // we should never reach here
  394. break;
  395. case NamedValueDword:
  396. {
  397. err = g_util.WriteRegDwordNamedValue(m_hRoot,
  398. (LPCTSTR)m_szKeyPath,
  399. (LPCTSTR)m_szValueName,
  400. pkt.nvdw.dwValue);
  401. }
  402. break;
  403. case SingleACL:
  404. {
  405. err = g_util.WriteRegSingleACL(m_hRoot,
  406. (LPCTSTR)m_szKeyPath,
  407. (LPCTSTR)m_szValueName,
  408. pkt.acl.pSec);
  409. }
  410. break;
  411. case RegKeyACL:
  412. err = g_util.WriteRegKeyACL(m_hRoot,
  413. pkt.racl.phClsids,
  414. pkt.racl.cClsids,
  415. pkt.racl.pSec,
  416. pkt.racl.pSecOrig);
  417. break;
  418. case Password:
  419. err = g_util.WriteLsaPassword(pkt.pw.appid,
  420. pkt.pw.szPassword);
  421. break;
  422. case ServiceIdentity:
  423. err = g_util.WriteSrvIdentity(pkt.si.szServiceName,
  424. pkt.si.szIdentity);
  425. break;
  426. }
  427. return err;
  428. }
  429. long CDataPacket::Read(HKEY hKey)
  430. {
  431. return 0;
  432. }
  433. int CDataPacket::Remove()
  434. {
  435. int err = ERROR_SUCCESS;
  436. if (m_fModified && m_fDelete)
  437. {
  438. switch (m_tagType)
  439. {
  440. case Empty:
  441. break;
  442. case SingleACL:
  443. case NamedValueDword:
  444. case NamedValueSz:
  445. case NamedValueMultiSz:
  446. if (m_fDeleteHive)
  447. g_util.DeleteRegKey(m_hRoot,(LPCTSTR)m_szKeyPath);
  448. else
  449. g_util.DeleteRegValue(m_hRoot,
  450. (LPCTSTR)m_szKeyPath,
  451. (LPCTSTR)m_szValueName);
  452. break;
  453. case RegKeyACL:
  454. err = g_util.WriteRegKeyACL(m_hRoot,
  455. pkt.racl.phClsids,
  456. pkt.racl.cClsids,
  457. pkt.racl.pSec,
  458. pkt.racl.pSecOrig);
  459. break;
  460. case Password:
  461. err = g_util.WriteLsaPassword(pkt.pw.appid,
  462. pkt.pw.szPassword);
  463. break;
  464. case ServiceIdentity:
  465. err = g_util.WriteSrvIdentity(pkt.si.szServiceName,
  466. pkt.si.szIdentity);
  467. break;
  468. }
  469. }
  470. return err;
  471. }
  472. //
  473. // ReportOutOfMemAndTerminate
  474. //
  475. // Dcomcnfg was not coded very well to handle out-of-memory
  476. // errors in certain spots. Rather than rip out and replace
  477. // lots of code to fix this properly, I am simply going to report
  478. // an error and terminate the process when this occurs. Dcomnfg
  479. // as of now only ships in the reskit, not in the os.
  480. //
  481. void CDataPacket::ReportOutOfMemAndTerminate()
  482. {
  483. CString sTitle;
  484. CString sMessage;
  485. if (sTitle.LoadString(IDS_FATALOUTOFMEMORYTITLE))
  486. {
  487. if (sMessage.LoadString(IDS_FATALOUTOFMEMORY))
  488. {
  489. MessageBoxW(NULL, sMessage, sTitle, MB_ICONWARNING | MB_OK | MB_TASKMODAL);
  490. }
  491. }
  492. TerminateProcess(GetCurrentProcess(), ERROR_NOT_ENOUGH_MEMORY);
  493. // will never get here
  494. return;
  495. }
  496. //*****************************************************************************
  497. //
  498. // class CRegSzNamedValueDp
  499. //
  500. // data packet for RegSZ named value
  501. //
  502. //*****************************************************************************
  503. int CRegSzNamedValueDp::Update()
  504. {
  505. int err = ERROR_SUCCESS;
  506. ASSERT(m_tagType == NamedValueSz);
  507. ASSERT(m_fModified);
  508. err = g_util.WriteRegSzNamedValue(m_hRoot,
  509. (LPCTSTR)m_szKeyPath,
  510. (LPCTSTR)m_szValueName,
  511. (LPCTSTR)m_szValue,
  512. m_szValue.GetLength() + 1);
  513. return err;
  514. }
  515. long CRegSzNamedValueDp::Read(HKEY hkey)
  516. {
  517. return 0;
  518. }
  519. CString CRegSzNamedValueDp::Value()
  520. {
  521. return m_szValue;
  522. }
  523. CRegSzNamedValueDp::CRegSzNamedValueDp(HKEY hRoot, TCHAR *szKeyPath, TCHAR *szValueName, TCHAR *szValue)
  524. : m_szValue(szValue)
  525. {
  526. m_tagType = NamedValueSz;
  527. m_hRoot = hRoot;
  528. m_szKeyPath = szKeyPath;
  529. m_szValueName = szValueName;
  530. }
  531. BOOL CDataPacket::IsIdentifiedBy(HKEY hRoot, TCHAR *szKeyPath, TCHAR *szValueName)
  532. {
  533. if (((m_tagType == NamedValueSz) &&
  534. (m_hRoot == hRoot) &&
  535. (m_szKeyPath == szKeyPath) &&
  536. (m_szValueName == szValueName)) ||
  537. ((m_tagType == NamedValueDword) &&
  538. (m_hRoot == hRoot) &&
  539. (m_szKeyPath == szKeyPath) &&
  540. (m_szValueName == szValueName) ))
  541. return TRUE;
  542. return FALSE;
  543. }
  544. BOOL CRegSzNamedValueDp::IsIdentifiedBy(HKEY hRoot, TCHAR * szKeyPath, TCHAR * szValueName)
  545. {
  546. if (((m_tagType == NamedValueSz) &&
  547. (m_hRoot == hRoot) &&
  548. (m_szKeyPath == szKeyPath) &&
  549. (m_szValueName == szValueName)))
  550. return TRUE;
  551. return FALSE;
  552. }
  553. //////////////////////////////////////////////////////////////////////
  554. // CRegMultiSzNamedValueDp Class
  555. //////////////////////////////////////////////////////////////////////
  556. //////////////////////////////////////////////////////////////////////
  557. // Construction/Destruction
  558. //////////////////////////////////////////////////////////////////////
  559. CRegMultiSzNamedValueDp::CRegMultiSzNamedValueDp(HKEY hRoot, TCHAR *szKeyPath, TCHAR *szValueName)
  560. {
  561. m_tagType = NamedValueMultiSz;
  562. m_hRoot = hRoot;
  563. m_szKeyPath = szKeyPath;
  564. m_szValueName = szValueName;
  565. }
  566. CRegMultiSzNamedValueDp::~CRegMultiSzNamedValueDp()
  567. {
  568. }
  569. BOOL CRegMultiSzNamedValueDp::IsIdentifiedBy(HKEY hRoot, TCHAR * szKeyPath, TCHAR * szValueName)
  570. {
  571. if (((m_tagType == NamedValueMultiSz) &&
  572. (m_hRoot == hRoot) &&
  573. (m_szKeyPath == szKeyPath) &&
  574. (m_szValueName == szValueName)))
  575. return TRUE;
  576. return FALSE;
  577. }
  578. int CRegMultiSzNamedValueDp::Update()
  579. {
  580. int err = ERROR_SUCCESS;
  581. ASSERT(m_tagType == NamedValueMultiSz);
  582. ASSERT(m_fModified);
  583. // build up string to save
  584. // calculate size of string
  585. int nSize=0, nIndex = 0;
  586. for (nIndex = 0; nIndex < m_strValues.GetSize(); nIndex ++)
  587. {
  588. CString sTmp = m_strValues.GetAt(nIndex);
  589. nSize += sTmp.GetLength()+1;
  590. }
  591. nSize += 2;
  592. // build up string to save
  593. TCHAR* lpszTmp = new TCHAR[nSize];
  594. if (lpszTmp)
  595. {
  596. int nOffset = 0;
  597. for (nIndex = 0; nIndex < m_strValues.GetSize(); nIndex ++)
  598. {
  599. CString sTmp = m_strValues.GetAt(nIndex);
  600. _tcscpy((TCHAR*)(&lpszTmp[nOffset]), (LPCTSTR) sTmp);
  601. nOffset += sTmp.GetLength()+1;
  602. }
  603. // finish with two nulls
  604. lpszTmp[nOffset++] = TEXT('\0');
  605. lpszTmp[nOffset++] = TEXT('\0');
  606. err = g_util.WriteRegMultiSzNamedValue(m_hRoot,
  607. (LPCTSTR)m_szKeyPath,
  608. (LPCTSTR)m_szValueName,
  609. lpszTmp,
  610. nOffset);
  611. delete lpszTmp;
  612. }
  613. return err;
  614. }
  615. void CRegMultiSzNamedValueDp::Clear()
  616. {
  617. m_strValues.RemoveAll();
  618. }
  619. long CRegMultiSzNamedValueDp::Read(HKEY hKey)
  620. {
  621. ASSERT(hKey != NULL);
  622. HKEY hkEndpoints = NULL;
  623. DWORD dwType = REG_MULTI_SZ;
  624. DWORD dwcbBuffer = 1024;
  625. TCHAR* pszBuffer = new TCHAR[1024];
  626. ASSERT(pszBuffer != NULL);
  627. // try to read values into default sized buffer
  628. LONG lErr = RegQueryValueEx(hKey,
  629. (LPCTSTR)m_szValueName,
  630. 0,
  631. &dwType,
  632. (LPBYTE)pszBuffer,
  633. &dwcbBuffer);
  634. // if buffer is not big enough, extend it and reread
  635. if (lErr == ERROR_MORE_DATA)
  636. {
  637. delete pszBuffer;
  638. DWORD dwNewSize = (dwcbBuffer + 1 / sizeof(TCHAR));
  639. pszBuffer = new TCHAR[dwNewSize];
  640. if (pszBuffer)
  641. dwcbBuffer = dwNewSize;
  642. lErr = RegQueryValueEx(m_hRoot,
  643. (LPCTSTR)m_szValueName,
  644. 0,
  645. &dwType,
  646. (LPBYTE)pszBuffer,
  647. &dwcbBuffer);
  648. }
  649. if ((lErr == ERROR_SUCCESS) &&
  650. (dwcbBuffer > 0) &&
  651. (dwType == REG_MULTI_SZ))
  652. {
  653. // parse each string
  654. TCHAR * lpszRegEntry = pszBuffer;
  655. while(lpszRegEntry && *lpszRegEntry)
  656. {
  657. // caclulate length of entry
  658. CString sTmp(lpszRegEntry);
  659. int nLenEntry = sTmp.GetLength();
  660. m_strValues.Add(sTmp);
  661. lpszRegEntry += nLenEntry+1;
  662. }
  663. }
  664. else if ((lErr != ERROR_SUCCESS) && (lErr != ERROR_FILE_NOT_FOUND))
  665. g_util.PostErrorMessage();
  666. delete pszBuffer;
  667. SetModified(FALSE);
  668. return lErr;
  669. }