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.

1649 lines
36 KiB

  1. //
  2. // Copyright (c) Microsoft Corporation 1993-1995
  3. //
  4. // rovcomm.c
  5. //
  6. // This files contains common utility and helper functions.
  7. //
  8. // History:
  9. // 08-06-93 ScottH Transferred from twin code
  10. // 04-26-95 ScottH Transferred and expanded from Briefcase code
  11. // 09-21-95 ScottH Ported to NT
  12. //
  13. #include "proj.h"
  14. #include "rovcomm.h"
  15. #include <debugmem.h>
  16. extern CHAR const FAR c_szNewline[];
  17. #define DEBUG_PRINT_BUFFER_LEN 1030
  18. #ifdef WINNT
  19. //
  20. // These are some helper functions for handling Unicode strings
  21. //
  22. /*----------------------------------------------------------
  23. Purpose: This function converts a wide-char string to a multi-byte
  24. string.
  25. If pszBuf is non-NULL and the converted string can fit in
  26. pszBuf, then *ppszAnsi will point to the given buffer.
  27. Otherwise, this function will allocate a buffer that can
  28. hold the converted string.
  29. If pszWide is NULL, then *ppszAnsi will be freed. Note
  30. that pszBuf must be the same pointer between the call
  31. that converted the string and the call that frees the
  32. string.
  33. Returns: TRUE
  34. FALSE (if out of memory)
  35. Cond: --
  36. */
  37. BOOL PUBLIC AnsiFromUnicode(
  38. LPSTR * ppszAnsi,
  39. LPCWSTR pwszWide, // NULL to clean up
  40. LPSTR pszBuf,
  41. int cchBuf)
  42. {
  43. BOOL bRet;
  44. // Convert the string?
  45. if (pwszWide)
  46. {
  47. // Yes; determine the converted string length
  48. int cch;
  49. LPSTR psz;
  50. cch = WideCharToMultiByte(CP_ACP, 0, pwszWide, -1, NULL, 0, NULL, NULL);
  51. // String too big, or is there no buffer?
  52. if (cch > cchBuf || NULL == pszBuf)
  53. {
  54. // Yes; allocate space
  55. cchBuf = cch + 1;
  56. psz = (LPSTR)ALLOCATE_MEMORY( CbFromCchA(cchBuf));
  57. }
  58. else
  59. {
  60. // No; use the provided buffer
  61. ASSERT(pszBuf);
  62. psz = pszBuf;
  63. }
  64. if (psz)
  65. {
  66. // Convert the string
  67. cch = WideCharToMultiByte(CP_ACP, 0, pwszWide, -1, psz, cchBuf, NULL, NULL);
  68. bRet = (0 < cch);
  69. }
  70. else
  71. {
  72. bRet = FALSE;
  73. }
  74. *ppszAnsi = psz;
  75. }
  76. else
  77. {
  78. // No; was this buffer allocated?
  79. if (*ppszAnsi && pszBuf != *ppszAnsi)
  80. {
  81. // Yes; clean up
  82. FREE_MEMORY(*ppszAnsi);
  83. *ppszAnsi = NULL;
  84. }
  85. bRet = TRUE;
  86. }
  87. return bRet;
  88. }
  89. /*----------------------------------------------------------
  90. Purpose: This function converts a multi-byte string to a
  91. wide-char string.
  92. If pszBuf is non-NULL and the converted string can fit in
  93. pszBuf, then *ppszWide will point to the given buffer.
  94. Otherwise, this function will allocate a buffer that can
  95. hold the converted string.
  96. If pszAnsi is NULL, then *ppszWide will be freed. Note
  97. that pszBuf must be the same pointer between the call
  98. that converted the string and the call that frees the
  99. string.
  100. Returns: TRUE
  101. FALSE (if out of memory)
  102. Cond: --
  103. */
  104. BOOL PUBLIC UnicodeFromAnsi(
  105. LPWSTR * ppwszWide,
  106. LPCSTR pszAnsi, // NULL to clean up
  107. LPWSTR pwszBuf,
  108. int cchBuf)
  109. {
  110. BOOL bRet;
  111. // Convert the string?
  112. if (pszAnsi)
  113. {
  114. // Yes; determine the converted string length
  115. int cch;
  116. LPWSTR pwsz;
  117. int cchAnsi = lstrlenA(pszAnsi)+1;
  118. cch = MultiByteToWideChar(CP_ACP, 0, pszAnsi, cchAnsi, NULL, 0);
  119. // String too big, or is there no buffer?
  120. if (cch > cchBuf || NULL == pwszBuf)
  121. {
  122. // Yes; allocate space
  123. cchBuf = cch + 1;
  124. pwsz = (LPWSTR)ALLOCATE_MEMORY( CbFromCchW(cchBuf));
  125. }
  126. else
  127. {
  128. // No; use the provided buffer
  129. ASSERT(pwszBuf);
  130. pwsz = pwszBuf;
  131. }
  132. if (pwsz)
  133. {
  134. // Convert the string
  135. cch = MultiByteToWideChar(CP_ACP, 0, pszAnsi, cchAnsi, pwsz, cchBuf);
  136. bRet = (0 < cch);
  137. }
  138. else
  139. {
  140. bRet = FALSE;
  141. }
  142. *ppwszWide = pwsz;
  143. }
  144. else
  145. {
  146. // No; was this buffer allocated?
  147. if (*ppwszWide && pwszBuf != *ppwszWide)
  148. {
  149. // Yes; clean up
  150. FREE_MEMORY(*ppwszWide);
  151. *ppwszWide = NULL;
  152. }
  153. bRet = TRUE;
  154. }
  155. return bRet;
  156. }
  157. #endif // WINNT
  158. #ifndef NOSTRING
  159. // WARNING: all of these APIs do not setup DS, so you can not access
  160. // any data in the default data seg of this DLL.
  161. //
  162. // do not create any global variables... talk to chrisg if you don't
  163. // understand this
  164. //#define STDCALL
  165. /*----------------------------------------------------------
  166. Purpose: Case sensitive character comparison for DBCS
  167. Returns: FALSE if they match, TRUE if no match
  168. Cond: --
  169. */
  170. BOOL NEAR ChrCmp(
  171. WORD w1,
  172. WORD wMatch)
  173. {
  174. /* Most of the time this won't match, so test it first for speed.
  175. */
  176. if (LOBYTE(w1) == LOBYTE(wMatch))
  177. {
  178. if (IsDBCSLeadByte(LOBYTE(w1)))
  179. {
  180. return(w1 != wMatch);
  181. }
  182. return FALSE;
  183. }
  184. return TRUE;
  185. }
  186. /*----------------------------------------------------------
  187. Purpose: Case insensitive character comparison for DBCS
  188. Returns: FALSE if match, TRUE if not
  189. Cond: --
  190. */
  191. BOOL NEAR ChrCmpI(
  192. WORD w1,
  193. WORD wMatch)
  194. {
  195. CHAR sz1[3], sz2[3];
  196. if (IsDBCSLeadByte(sz1[0] = LOBYTE(w1)))
  197. {
  198. sz1[1] = HIBYTE(w1);
  199. sz1[2] = '\0';
  200. }
  201. else
  202. sz1[1] = '\0';
  203. *(WORD FAR *)sz2 = wMatch;
  204. sz2[2] = '\0';
  205. return lstrcmpiA(sz1, sz2);
  206. }
  207. #ifndef WIN32
  208. /*----------------------------------------------------------
  209. Purpose: strnicmp
  210. Swiped from the C 7.0 runtime sources.
  211. Returns:
  212. Cond:
  213. */
  214. int PUBLIC lstrnicmp(
  215. LPCSTR psz1,
  216. LPCSTR psz2,
  217. UINT count)
  218. {
  219. int ch1;
  220. int ch2;
  221. int result = 0;
  222. if (count)
  223. {
  224. do
  225. {
  226. ch1 = (int)LOWORD(AnsiLower((LPSTR)MAKELONG(*psz1, 0)));
  227. ch2 = (int)LOWORD(AnsiLower((LPSTR)MAKELONG(*psz2, 0)));
  228. psz1 = AnsiNext(psz1);
  229. psz2 = AnsiNext(psz2);
  230. } while (--count && ch1 && ch2 && !ChrCmp((WORD)ch1, (WORD)ch2));
  231. result = ch1 - ch2;
  232. }
  233. return(result);
  234. }
  235. /*----------------------------------------------------------
  236. Purpose: strncmp
  237. Swiped from the C 7.0 runtime sources.
  238. Returns:
  239. Cond:
  240. */
  241. int PUBLIC lstrncmp(
  242. LPCSTR psz1,
  243. LPCSTR psz2,
  244. UINT count)
  245. {
  246. int ch1;
  247. int ch2;
  248. int result = 0;
  249. if (count)
  250. {
  251. do
  252. {
  253. ch1 = (int)*psz1;
  254. ch2 = (int)*psz2;
  255. psz1 = AnsiNext(psz1);
  256. psz2 = AnsiNext(psz2);
  257. } while (--count && ch1 && ch2 && !ChrCmp((WORD)ch1, (WORD)ch2));
  258. result = ch1 - ch2;
  259. }
  260. return(result);
  261. }
  262. #endif // WIN32
  263. #ifdef WINNT
  264. /*----------------------------------------------------------
  265. Purpose: Wide-char wrapper for AnsiToIntA.
  266. Returns: see AnsiToIntA
  267. Cond: --
  268. */
  269. BOOL PUBLIC AnsiToIntW(
  270. LPCWSTR pwszString,
  271. int FAR * piRet)
  272. {
  273. CHAR szBuf[MAX_BUF];
  274. LPSTR pszString;
  275. BOOL bRet;
  276. pszString = NULL;
  277. bRet = AnsiFromUnicode(&pszString, pwszString, szBuf, ARRAYSIZE(szBuf));
  278. if (bRet)
  279. {
  280. if (pszString == NULL)
  281. {
  282. bRet = FALSE;
  283. } else
  284. {
  285. bRet = AnsiToIntA(pszString, piRet);
  286. AnsiFromUnicode(&pszString, NULL, szBuf, 0);
  287. }
  288. }
  289. return bRet;
  290. }
  291. /*----------------------------------------------------------
  292. Purpose: Wide-char wrapper for AnsiChrA.
  293. Returns: see AnsiChrA
  294. Cond: --
  295. */
  296. LPWSTR PUBLIC AnsiChrW(
  297. LPCWSTR pwsz,
  298. WORD wMatch)
  299. {
  300. for ( ; *pwsz; pwsz = CharNextW(pwsz))
  301. {
  302. if (!ChrCmp(*(WORD FAR *)pwsz, wMatch))
  303. return (LPWSTR)pwsz;
  304. }
  305. return NULL;
  306. }
  307. #endif // WINNT
  308. /*----------------------------------------------------------
  309. Purpose: Find last occurrence (case sensitive) of wide
  310. character in wide-char string.
  311. Returns: Pointer to the last occurrence of character in
  312. string or NULL if character is not found.
  313. Cond: --
  314. */
  315. LPWSTR
  316. PUBLIC
  317. AnsiRChrW(
  318. LPCWSTR pwsz,
  319. WORD wMatch)
  320. {
  321. LPWSTR pwszEnd;
  322. if (pwsz && *pwsz)
  323. {
  324. for (pwszEnd = (LPWSTR)pwsz + lstrlen(pwsz) - 1;
  325. pwsz <= pwszEnd;
  326. pwszEnd = CharPrevW(pwsz, pwszEnd))
  327. {
  328. if (!ChrCmp(*(WORD FAR *)pwszEnd, wMatch))
  329. return(pwszEnd);
  330. // CharPrevW() won't go to char preceding pwsz...
  331. if (pwsz == pwszEnd)
  332. break;
  333. }
  334. }
  335. return(NULL);
  336. }
  337. /*----------------------------------------------------------
  338. Purpose: My verion of atoi. Supports hexadecimal too.
  339. If this function returns FALSE, *piRet is set to 0.
  340. Returns: TRUE if the string is a number, or contains a partial number
  341. FALSE if the string is not a number
  342. Cond: --
  343. */
  344. BOOL PUBLIC AnsiToIntA(
  345. LPCSTR pszString,
  346. int FAR * piRet)
  347. {
  348. #define IS_DIGIT(ch) InRange(ch, '0', '9')
  349. BOOL bRet;
  350. int n;
  351. BOOL bNeg = FALSE;
  352. LPCSTR psz;
  353. LPCSTR pszAdj;
  354. // Skip leading whitespace
  355. //
  356. for (psz = pszString; *psz == ' ' || *psz == '\n' || *psz == '\t'; psz = AnsiNext(psz))
  357. ;
  358. // Determine possible explicit signage
  359. //
  360. if (*psz == '+' || *psz == '-')
  361. {
  362. bNeg = (*psz == '+') ? FALSE : TRUE;
  363. psz++;
  364. }
  365. // Or is this hexadecimal?
  366. //
  367. pszAdj = AnsiNext(psz);
  368. if (*psz == '0' && (*pszAdj == 'x' || *pszAdj == 'X'))
  369. {
  370. // Yes
  371. // (Never allow negative sign with hexadecimal numbers)
  372. bNeg = FALSE;
  373. psz = AnsiNext(pszAdj);
  374. pszAdj = psz;
  375. // Do the conversion
  376. //
  377. for (n = 0; ; psz = AnsiNext(psz))
  378. {
  379. if (IS_DIGIT(*psz))
  380. n = 0x10 * n + *psz - '0';
  381. else
  382. {
  383. CHAR ch = *psz;
  384. int n2;
  385. if (ch >= 'a')
  386. ch -= 'a' - 'A';
  387. n2 = ch - 'A' + 0xA;
  388. if (n2 >= 0xA && n2 <= 0xF)
  389. n = 0x10 * n + n2;
  390. else
  391. break;
  392. }
  393. }
  394. // Return TRUE if there was at least one digit
  395. bRet = (psz != pszAdj);
  396. }
  397. else
  398. {
  399. // No
  400. pszAdj = psz;
  401. // Do the conversion
  402. for (n = 0; IS_DIGIT(*psz); psz = AnsiNext(psz))
  403. n = 10 * n + *psz - '0';
  404. // Return TRUE if there was at least one digit
  405. bRet = (psz != pszAdj);
  406. }
  407. *piRet = bNeg ? -n : n;
  408. return bRet;
  409. }
  410. /*----------------------------------------------------------
  411. Purpose: Find first occurrence of character in string
  412. Returns: Pointer to the first occurrence of ch in
  413. Cond: --
  414. */
  415. LPSTR PUBLIC AnsiChrA(
  416. LPCSTR psz,
  417. WORD wMatch)
  418. {
  419. for ( ; *psz; psz = AnsiNext(psz))
  420. {
  421. if (!ChrCmp(*(WORD FAR *)psz, wMatch))
  422. return (LPSTR)psz;
  423. }
  424. return NULL;
  425. }
  426. #endif // NOSTRING
  427. #ifndef NODIALOGHELPER
  428. /*----------------------------------------------------------
  429. Purpose: Sets the rectangle with the bounding extent of the given string.
  430. Returns: Rectangle
  431. Cond: --
  432. */
  433. void PUBLIC SetRectFromExtentW(
  434. HDC hdc,
  435. LPRECT lprect,
  436. LPCWSTR lpcwsz)
  437. {
  438. SIZE size;
  439. GetTextExtentPointW(hdc, lpcwsz, lstrlenW(lpcwsz), &size);
  440. SetRect(lprect, 0, 0, size.cx, size.cy);
  441. }
  442. /*----------------------------------------------------------
  443. Purpose: Sets the rectangle with the bounding extent of the given string.
  444. Returns: Rectangle
  445. Cond: --
  446. */
  447. void PUBLIC SetRectFromExtentA(
  448. HDC hdc,
  449. LPRECT lprect,
  450. LPCSTR lpcsz)
  451. {
  452. SIZE size;
  453. GetTextExtentPointA(hdc, lpcsz, lstrlenA(lpcsz), &size);
  454. SetRect(lprect, 0, 0, size.cx, size.cy);
  455. }
  456. #endif // NODIALOGHELPER
  457. #ifndef NODRAWTEXT
  458. #pragma data_seg(DATASEG_READONLY)
  459. CHAR const FAR c_szEllipses[] = "...";
  460. #pragma data_seg()
  461. // Global variables
  462. int g_cxLabelMargin = 0;
  463. int g_cxBorder = 0;
  464. int g_cyBorder = 0;
  465. COLORREF g_clrHighlightText = 0;
  466. COLORREF g_clrHighlight = 0;
  467. COLORREF g_clrWindowText = 0;
  468. COLORREF g_clrWindow = 0;
  469. HBRUSH g_hbrHighlight = 0;
  470. HBRUSH g_hbrWindow = 0;
  471. /*----------------------------------------------------------
  472. Purpose: Get the system metrics we need
  473. Returns: --
  474. Cond: --
  475. */
  476. void PUBLIC GetCommonMetrics(
  477. WPARAM wParam) // wParam from WM_WININICHANGE
  478. {
  479. if ((wParam == 0) || (wParam == SPI_SETNONCLIENTMETRICS))
  480. {
  481. g_cxBorder = GetSystemMetrics(SM_CXBORDER);
  482. g_cyBorder = GetSystemMetrics(SM_CYBORDER);
  483. g_cxLabelMargin = (g_cxBorder * 2);
  484. }
  485. }
  486. /*----------------------------------------------------------
  487. Purpose: Sees whether the entire string will fit in *prc.
  488. If not, compute the numbder of chars that will fit
  489. (including ellipses). Returns length of string in
  490. *pcchDraw.
  491. Taken from COMMCTRL.
  492. Returns: TRUE if the string needed ellipses
  493. Cond: --
  494. */
  495. BOOL PRIVATE NeedsEllipses(
  496. HDC hdc,
  497. LPCSTR pszText,
  498. RECT * prc,
  499. int * pcchDraw,
  500. int cxEllipses)
  501. {
  502. int cchText;
  503. int cxRect;
  504. int ichMin, ichMax, ichMid;
  505. SIZE siz;
  506. cxRect = prc->right - prc->left;
  507. cchText = lstrlenA(pszText);
  508. if (cchText == 0)
  509. {
  510. *pcchDraw = cchText;
  511. return FALSE;
  512. }
  513. GetTextExtentPointA(hdc, pszText, cchText, &siz);
  514. if (siz.cx <= cxRect)
  515. {
  516. *pcchDraw = cchText;
  517. return FALSE;
  518. }
  519. cxRect -= cxEllipses;
  520. // If no room for ellipses, always show first character.
  521. //
  522. ichMax = 1;
  523. if (cxRect > 0)
  524. {
  525. // Binary search to find character that will fit
  526. ichMin = 0;
  527. ichMax = cchText;
  528. while (ichMin < ichMax)
  529. {
  530. // Be sure to round up, to make sure we make progress in
  531. // the loop if ichMax == ichMin + 1.
  532. //
  533. ichMid = (ichMin + ichMax + 1) / 2;
  534. GetTextExtentPointA(hdc, &pszText[ichMin], ichMid - ichMin, &siz);
  535. if (siz.cx < cxRect)
  536. {
  537. ichMin = ichMid;
  538. cxRect -= siz.cx;
  539. }
  540. else if (siz.cx > cxRect)
  541. {
  542. ichMax = ichMid - 1;
  543. }
  544. else
  545. {
  546. // Exact match up up to ichMid: just exit.
  547. //
  548. ichMax = ichMid;
  549. break;
  550. }
  551. }
  552. // Make sure we always show at least the first character...
  553. //
  554. if (ichMax < 1)
  555. ichMax = 1;
  556. }
  557. *pcchDraw = ichMax;
  558. return TRUE;
  559. }
  560. #define CCHELLIPSES 3
  561. #define DT_LVWRAP (DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL)
  562. #ifdef WINNT
  563. /*----------------------------------------------------------
  564. Purpose: Wide-char wrapper for MyDrawTextA.
  565. Returns: see MyDrawTextA
  566. Cond: --
  567. */
  568. void PUBLIC MyDrawTextW(
  569. HDC hdc,
  570. LPCWSTR pwszText,
  571. RECT FAR* prc,
  572. UINT flags,
  573. int cyChar,
  574. int cxEllipses,
  575. COLORREF clrText,
  576. COLORREF clrTextBk)
  577. {
  578. CHAR szBuf[MAX_BUF];
  579. LPSTR pszText;
  580. BOOL bRet;
  581. pszText = NULL;
  582. bRet = AnsiFromUnicode(&pszText, pwszText, szBuf, ARRAYSIZE(szBuf));
  583. if (bRet)
  584. {
  585. MyDrawTextA(hdc, pszText, prc, flags, cyChar, cxEllipses, clrText, clrTextBk);
  586. AnsiFromUnicode(&pszText, NULL, szBuf, 0);
  587. }
  588. }
  589. #endif // WINNT
  590. /*----------------------------------------------------------
  591. Purpose: Draws text the shell's way.
  592. Taken from COMMCTRL.
  593. Returns: --
  594. Cond: This function requires TRANSPARENT background mode
  595. and a properly selected font.
  596. */
  597. void PUBLIC MyDrawTextA(
  598. HDC hdc,
  599. LPCSTR pszText,
  600. RECT FAR* prc,
  601. UINT flags,
  602. int cyChar,
  603. int cxEllipses,
  604. COLORREF clrText,
  605. COLORREF clrTextBk)
  606. {
  607. int cchText;
  608. COLORREF clrSave;
  609. COLORREF clrSaveBk;
  610. UINT uETOFlags = 0;
  611. RECT rc;
  612. CHAR ach[MAX_PATH + CCHELLIPSES];
  613. // REVIEW: Performance idea:
  614. // We could cache the currently selected text color
  615. // so we don't have to set and restore it each time
  616. // when the color is the same.
  617. //
  618. if (!pszText)
  619. return;
  620. rc = *prc;
  621. // If needed, add in a little extra margin...
  622. //
  623. if (IsFlagSet(flags, MDT_EXTRAMARGIN))
  624. {
  625. rc.left += g_cxLabelMargin * 3;
  626. rc.right -= g_cxLabelMargin * 3;
  627. }
  628. else
  629. {
  630. rc.left += g_cxLabelMargin;
  631. rc.right -= g_cxLabelMargin;
  632. }
  633. if (IsFlagSet(flags, MDT_ELLIPSES) &&
  634. NeedsEllipses(hdc, pszText, &rc, &cchText, cxEllipses))
  635. {
  636. hmemcpy(ach, pszText, cchText);
  637. lstrcpyA(ach + cchText, c_szEllipses);
  638. pszText = ach;
  639. // Left-justify, in case there's no room for all of ellipses
  640. //
  641. ClearFlag(flags, (MDT_RIGHT | MDT_CENTER));
  642. SetFlag(flags, MDT_LEFT);
  643. cchText += CCHELLIPSES;
  644. }
  645. else
  646. {
  647. cchText = lstrlenA(pszText);
  648. }
  649. if (IsFlagSet(flags, MDT_TRANSPARENT))
  650. {
  651. clrSave = SetTextColor(hdc, 0x000000);
  652. }
  653. else
  654. {
  655. uETOFlags |= ETO_OPAQUE;
  656. if (IsFlagSet(flags, MDT_SELECTED))
  657. {
  658. clrSave = SetTextColor(hdc, g_clrHighlightText);
  659. clrSaveBk = SetBkColor(hdc, g_clrHighlight);
  660. if (IsFlagSet(flags, MDT_DRAWTEXT))
  661. {
  662. FillRect(hdc, prc, g_hbrHighlight);
  663. }
  664. }
  665. else
  666. {
  667. if (clrText == CLR_DEFAULT && clrTextBk == CLR_DEFAULT)
  668. {
  669. clrSave = SetTextColor(hdc, g_clrWindowText);
  670. clrSaveBk = SetBkColor(hdc, g_clrWindow);
  671. if (IsFlagSet(flags, MDT_DRAWTEXT | MDT_DESELECTED))
  672. {
  673. FillRect(hdc, prc, g_hbrWindow);
  674. }
  675. }
  676. else
  677. {
  678. HBRUSH hbr;
  679. if (clrText == CLR_DEFAULT)
  680. clrText = g_clrWindowText;
  681. if (clrTextBk == CLR_DEFAULT)
  682. clrTextBk = g_clrWindow;
  683. clrSave = SetTextColor(hdc, clrText);
  684. clrSaveBk = SetBkColor(hdc, clrTextBk);
  685. if (IsFlagSet(flags, MDT_DRAWTEXT | MDT_DESELECTED))
  686. {
  687. hbr = CreateSolidBrush(GetNearestColor(hdc, clrTextBk));
  688. if (hbr)
  689. {
  690. FillRect(hdc, prc, hbr);
  691. DeleteObject(hbr);
  692. }
  693. else
  694. FillRect(hdc, prc, GetStockObject(WHITE_BRUSH));
  695. }
  696. }
  697. }
  698. }
  699. // If we want the item to display as if it was depressed, we will
  700. // offset the text rectangle down and to the left
  701. if (IsFlagSet(flags, MDT_DEPRESSED))
  702. OffsetRect(&rc, g_cxBorder, g_cyBorder);
  703. if (IsFlagSet(flags, MDT_DRAWTEXT))
  704. {
  705. UINT uDTFlags = DT_LVWRAP;
  706. if (IsFlagClear(flags, MDT_CLIPPED))
  707. uDTFlags |= DT_NOCLIP;
  708. DrawTextA(hdc, pszText, cchText, &rc, uDTFlags);
  709. }
  710. else
  711. {
  712. if (IsFlagClear(flags, MDT_LEFT))
  713. {
  714. SIZE siz;
  715. GetTextExtentPointA(hdc, pszText, cchText, &siz);
  716. if (IsFlagSet(flags, MDT_CENTER))
  717. rc.left = (rc.left + rc.right - siz.cx) / 2;
  718. else
  719. {
  720. ASSERT(IsFlagSet(flags, MDT_RIGHT));
  721. rc.left = rc.right - siz.cx;
  722. }
  723. }
  724. if (IsFlagSet(flags, MDT_VCENTER))
  725. {
  726. // Center vertically
  727. rc.top += (rc.bottom - rc.top - cyChar) / 2;
  728. }
  729. if (IsFlagSet(flags, MDT_CLIPPED))
  730. uETOFlags |= ETO_CLIPPED;
  731. ExtTextOutA(hdc, rc.left, rc.top, uETOFlags, prc, pszText, cchText, NULL);
  732. }
  733. if (flags & (MDT_SELECTED | MDT_DESELECTED | MDT_TRANSPARENT))
  734. {
  735. SetTextColor(hdc, clrSave);
  736. if (IsFlagClear(flags, MDT_TRANSPARENT))
  737. SetBkColor(hdc, clrSaveBk);
  738. }
  739. }
  740. #endif // NODRAWTEXT
  741. #ifndef NOMESSAGESTRING
  742. typedef va_list * LPVA_LIST;
  743. #ifdef WINNT
  744. #define IsPointerResouceId(_p) (((ULONG_PTR)_p) <= 0xffff)
  745. /*----------------------------------------------------------
  746. Purpose: Wide-char version of ConstructVMessageStringA
  747. Returns: see ConstructVMessageStringA
  748. Cond: --
  749. */
  750. LPWSTR PUBLIC ConstructVMessageStringW(
  751. HINSTANCE hinst,
  752. LPCWSTR pwszMsg,
  753. va_list FAR * ArgList)
  754. {
  755. WCHAR wszTemp[MAX_BUF];
  756. LPWSTR pwszRet;
  757. LPWSTR pwszRes;
  758. if (!IsPointerResouceId(pwszMsg)) {
  759. pwszRes = (LPWSTR)pwszMsg;
  760. } else {
  761. if ((((ULONG_PTR)pwszMsg) != 0) && LoadStringW(hinst, (DWORD)(ULONG_PTR)pwszMsg, wszTemp, ARRAYSIZE(wszTemp))) {
  762. pwszRes = wszTemp;
  763. } else {
  764. pwszRes = NULL;
  765. }
  766. }
  767. if (pwszRes) {
  768. if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
  769. pwszRes, 0, 0, (LPWSTR)&pwszRet, 0, (LPVA_LIST)ArgList))
  770. {
  771. pwszRet = NULL;
  772. }
  773. }
  774. else
  775. {
  776. // Bad parameter
  777. pwszRet = NULL;
  778. }
  779. return pwszRet; // free with LocalFree()
  780. }
  781. /*----------------------------------------------------------
  782. Purpose: Wide-char version of ConstructMessageA.
  783. Returns: see ConstructMessageA
  784. Cond: --
  785. */
  786. BOOL CPUBLIC ConstructMessageW(
  787. LPWSTR FAR * ppwsz,
  788. HINSTANCE hinst,
  789. LPCWSTR pwszMsg, ...)
  790. {
  791. BOOL bRet;
  792. LPWSTR pwszRet;
  793. va_list ArgList;
  794. va_start(ArgList, pwszMsg);
  795. pwszRet = ConstructVMessageStringW(hinst, pwszMsg, &ArgList);
  796. va_end(ArgList);
  797. *ppwsz = NULL;
  798. if (pwszRet)
  799. {
  800. bRet = SetStringW(ppwsz, pwszRet);
  801. LocalFree(pwszRet);
  802. }
  803. else
  804. bRet = FALSE;
  805. return bRet;
  806. }
  807. /*----------------------------------------------------------
  808. Purpose: Wide-char version of MsgBoxA
  809. Returns: See MsgBoxA
  810. Cond: --
  811. */
  812. int CPUBLIC MsgBoxW(
  813. HINSTANCE hinst,
  814. HWND hwndOwner,
  815. LPCWSTR pwszText,
  816. LPCWSTR pwszCaption,
  817. HICON hicon, // May be NULL
  818. DWORD dwStyle, ...)
  819. {
  820. int iRet = -1;
  821. int ids;
  822. WCHAR wszCaption[MAX_BUF];
  823. LPWSTR pwszRet;
  824. va_list ArgList;
  825. va_start(ArgList, dwStyle);
  826. pwszRet = ConstructVMessageStringW(hinst, pwszText, &ArgList);
  827. va_end(ArgList);
  828. if (pwszRet)
  829. {
  830. // Is pszCaption a resource ID?
  831. if (IsPointerResouceId(pwszCaption))
  832. {
  833. // Yes; load it
  834. ids = LOWORD(pwszCaption);
  835. SzFromIDSW(hinst, ids, wszCaption, ARRAYSIZE(wszCaption));
  836. pwszCaption = wszCaption;
  837. }
  838. // Invoke dialog
  839. if (pwszCaption)
  840. {
  841. MSGBOXPARAMSW mbp;
  842. mbp.cbSize = sizeof(mbp);
  843. mbp.hwndOwner = hwndOwner;
  844. mbp.hInstance = hinst;
  845. mbp.lpszText = pwszRet;
  846. mbp.lpszCaption = pwszCaption;
  847. mbp.dwStyle = dwStyle | MB_SETFOREGROUND;
  848. mbp.lpszIcon = MAKEINTRESOURCEW(hicon);
  849. mbp.lpfnMsgBoxCallback = NULL;
  850. mbp.dwLanguageId = LANG_NEUTRAL;
  851. iRet = MessageBoxIndirectW(&mbp);
  852. }
  853. LocalFree(pwszRet);
  854. }
  855. return iRet;
  856. }
  857. #endif // WINNT
  858. /*----------------------------------------------------------
  859. Purpose: Load the string (if necessary) and format the string
  860. properly.
  861. Returns: A pointer to the allocated string containing the formatted
  862. message or
  863. NULL if out of memory
  864. Cond: free pointer with FREE_MEMORY()
  865. */
  866. LPSTR PUBLIC ConstructVMessageStringA(
  867. HINSTANCE hinst,
  868. LPCSTR pszMsg,
  869. va_list FAR * ArgList)
  870. {
  871. CHAR szTemp[MAX_BUF];
  872. LPSTR pszRet;
  873. LPSTR pszRes;
  874. if (!IsPointerResouceId(pszMsg)) {
  875. pszRes = (LPSTR)pszMsg;
  876. } else {
  877. if ((((ULONG_PTR)pszMsg) != 0) && LoadStringA(hinst, (DWORD)(ULONG_PTR)pszMsg, szTemp, ARRAYSIZE(szTemp))) {
  878. pszRes = szTemp;
  879. } else {
  880. pszRes = NULL;
  881. }
  882. }
  883. if (pszRes)
  884. {
  885. if (!FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
  886. pszRes, 0, 0, (LPSTR)&pszRet, 0, (LPVA_LIST)ArgList))
  887. {
  888. pszRet = NULL;
  889. }
  890. }
  891. else
  892. {
  893. // Bad parameter
  894. pszRet = NULL;
  895. }
  896. return pszRet; // free with FREE_MEMORY()
  897. }
  898. /*----------------------------------------------------------
  899. Purpose: Constructs a formatted string. The returned string
  900. must be freed using GFree().
  901. Returns: TRUE on success
  902. Cond: Free pointer with GFree()
  903. */
  904. BOOL CPUBLIC ConstructMessageA(
  905. LPSTR FAR * ppsz,
  906. HINSTANCE hinst,
  907. LPCSTR pszMsg, ...)
  908. {
  909. BOOL bRet;
  910. LPSTR pszRet;
  911. va_list ArgList;
  912. va_start(ArgList, pszMsg);
  913. pszRet = ConstructVMessageStringA(hinst, pszMsg, &ArgList);
  914. va_end(ArgList);
  915. *ppsz = NULL;
  916. if (pszRet)
  917. {
  918. bRet = SetStringA(ppsz, pszRet);
  919. LocalFree(pszRet);
  920. }
  921. else
  922. bRet = FALSE;
  923. return bRet;
  924. }
  925. /*----------------------------------------------------------
  926. Purpose: Invoke a message box.
  927. Returns: ID of button that terminated the dialog
  928. Cond: --
  929. */
  930. int CPUBLIC MsgBoxA(
  931. HINSTANCE hinst,
  932. HWND hwndOwner,
  933. LPCSTR pszText,
  934. LPCSTR pszCaption,
  935. HICON hicon, // May be NULL
  936. DWORD dwStyle, ...)
  937. {
  938. int iRet = -1;
  939. int ids;
  940. CHAR szCaption[MAX_BUF];
  941. LPSTR pszRet;
  942. va_list ArgList;
  943. va_start(ArgList, dwStyle);
  944. pszRet = ConstructVMessageStringA(hinst, pszText, &ArgList);
  945. va_end(ArgList);
  946. if (pszRet)
  947. {
  948. // Is pszCaption a resource ID?
  949. if (IsPointerResouceId(pszCaption))
  950. {
  951. // Yes; load it
  952. ids = LOWORD(pszCaption);
  953. SzFromIDSA(hinst, ids, szCaption, SIZECHARS(szCaption));
  954. pszCaption = szCaption;
  955. }
  956. // Invoke dialog
  957. if (pszCaption)
  958. {
  959. #ifdef WIN32
  960. MSGBOXPARAMSA mbp;
  961. mbp.cbSize = sizeof(mbp);
  962. mbp.hwndOwner = hwndOwner;
  963. mbp.hInstance = hinst;
  964. mbp.lpszText = pszRet;
  965. mbp.lpszCaption = pszCaption;
  966. mbp.dwStyle = dwStyle | MB_SETFOREGROUND;
  967. mbp.lpszIcon = MAKEINTRESOURCEA(hicon);
  968. mbp.lpfnMsgBoxCallback = NULL;
  969. mbp.dwLanguageId = LANG_NEUTRAL;
  970. iRet = MessageBoxIndirectA(&mbp);
  971. #else // WIN32
  972. iRet = MessageBox(hwndOwner, pszRet, pszCaption, LOWORD(dwStyle));
  973. #endif
  974. }
  975. LocalFree(pszRet);
  976. }
  977. return iRet;
  978. }
  979. #endif // NOMESSAGESTRING
  980. #if !defined(NODEBUGHELP) && defined(DEBUG)
  981. // Globals
  982. DWORD g_dwBreakFlags = 0;
  983. DWORD g_dwDumpFlags = 0;
  984. DWORD g_dwTraceFlags = 0;
  985. LONG g_dwIndent = 0;
  986. #pragma data_seg(DATASEG_READONLY)
  987. #ifdef WINNT
  988. extern WCHAR const FAR c_wszNewline[];
  989. extern WCHAR const FAR c_wszTrace[];
  990. extern WCHAR const FAR c_wszAssertFailed[];
  991. #endif // WINNT
  992. extern CHAR const FAR c_szNewline[];
  993. extern CHAR const FAR c_szTrace[];
  994. extern CHAR const FAR c_szAssertFailed[];
  995. #pragma data_seg()
  996. /*----------------------------------------------------------
  997. Purpose: Return English reason for the debug break
  998. Returns: String
  999. Cond: --
  1000. */
  1001. LPCSTR PRIVATE GetReasonString(
  1002. DWORD flag) // One of BF_ flags
  1003. {
  1004. LPCSTR psz;
  1005. if (IsFlagSet(flag, BF_ONOPEN))
  1006. psz = "BREAK ON OPEN\r\n";
  1007. else if (IsFlagSet(flag, BF_ONCLOSE))
  1008. psz = "BREAK ON CLOSE\r\n";
  1009. else if (IsFlagSet(flag, BF_ONRUNONCE))
  1010. psz = "BREAK ON RUNONCE\r\n";
  1011. else if (IsFlagSet(flag, BF_ONVALIDATE))
  1012. psz = "BREAK ON VALIDATION FAILURE\r\n";
  1013. else if (IsFlagSet(flag, BF_ONTHREADATT))
  1014. psz = "BREAK ON THREAD ATTACH\r\n";
  1015. else if (IsFlagSet(flag, BF_ONTHREADDET))
  1016. psz = "BREAK ON THREAD DETACH\r\n";
  1017. else if (IsFlagSet(flag, BF_ONPROCESSATT))
  1018. psz = "BREAK ON PROCESS ATTACH\r\n";
  1019. else if (IsFlagSet(flag, BF_ONPROCESSDET))
  1020. psz = "BREAK ON PROCESS DETACH\r\n";
  1021. else if (IsFlagSet(flag, BF_ONAPIENTER))
  1022. psz = "BREAK ON API ENTER\r\n";
  1023. else
  1024. psz = c_szNewline;
  1025. return psz;
  1026. }
  1027. /*----------------------------------------------------------
  1028. Purpose: Perform a debug break based on the flag
  1029. Returns: --
  1030. Cond: --
  1031. */
  1032. void PUBLIC CommonDebugBreak(
  1033. DWORD flag) // One of BF_ flags
  1034. {
  1035. if (IsFlagSet(g_dwBreakFlags, flag))
  1036. {
  1037. TRACE_MSG(TF_ALWAYS, GetReasonString(flag));
  1038. DebugBreak();
  1039. }
  1040. }
  1041. /*----------------------------------------------------------
  1042. Purpose: Assert failed
  1043. Returns: --
  1044. Cond: --
  1045. */
  1046. void PUBLIC CommonAssertFailed(
  1047. LPCSTR pszFile,
  1048. int line)
  1049. {
  1050. LPCSTR psz;
  1051. CHAR ach[256];
  1052. // Strip off path info from filename string, if present.
  1053. //
  1054. for (psz = pszFile + lstrlenA(pszFile); psz != pszFile; psz=AnsiPrev(pszFile, psz))
  1055. {
  1056. if ((AnsiPrev(pszFile, psz) != (psz-2)) && *(psz - 1) == '\\')
  1057. break;
  1058. }
  1059. wsprintfA(ach, c_szAssertFailed, psz, line);
  1060. OutputDebugStringA(ach);
  1061. if (IsFlagSet(g_dwBreakFlags, BF_ONVALIDATE))
  1062. DebugBreak();
  1063. }
  1064. #ifdef WINNT
  1065. /*----------------------------------------------------------
  1066. Purpose: Determine id debug should be displayed
  1067. Returns: --
  1068. Cond: --
  1069. */
  1070. BOOL WINAPI
  1071. DisplayDebug(
  1072. DWORD flag
  1073. )
  1074. {
  1075. return (IsFlagSet(g_dwTraceFlags, flag));
  1076. }
  1077. /*----------------------------------------------------------
  1078. Purpose: Wide-char version of CommonAssertMsgA
  1079. Returns: --
  1080. Cond: --
  1081. */
  1082. void CPUBLIC CommonAssertMsgW(
  1083. BOOL f,
  1084. LPCWSTR pwszMsg, ...)
  1085. {
  1086. WCHAR ach[DEBUG_PRINT_BUFFER_LEN]; // Largest path plus extra
  1087. va_list vArgs;
  1088. if (!f)
  1089. {
  1090. int cch;
  1091. lstrcpyW(ach, c_wszTrace);
  1092. cch = lstrlenW(ach);
  1093. va_start(vArgs, pwszMsg);
  1094. wvsprintfW(&ach[cch], pwszMsg, vArgs);
  1095. va_end(vArgs);
  1096. OutputDebugStringW(ach);
  1097. OutputDebugStringW(c_wszNewline);
  1098. }
  1099. }
  1100. /*----------------------------------------------------------
  1101. Purpose: Wide-char version of CommonDebugMsgA
  1102. Returns: --
  1103. Cond: --
  1104. */
  1105. void CPUBLIC CommonDebugMsgW(
  1106. DWORD flag,
  1107. LPCSTR pszMsg, ...)
  1108. {
  1109. WCHAR ach[DEBUG_PRINT_BUFFER_LEN]; // Largest path plus extra
  1110. va_list vArgs;
  1111. DWORD dwLastError = GetLastError (); // Save the last error
  1112. if (IsFlagSet(g_dwTraceFlags, flag))
  1113. {
  1114. int cch;
  1115. WCHAR wszBuf[MAX_BUF];
  1116. LPWSTR pwsz;
  1117. WCHAR wszBlank[] = L" ";
  1118. wszBlank[g_dwIndent < 0 ? 0 : g_dwIndent] = L'\0';
  1119. #ifdef PROFILE_TRACES
  1120. const static WCHAR szTemplate[]=TEXT("[%lu] ");
  1121. static DWORD dwTickLast;
  1122. static DWORD dwTickNow = 0;
  1123. if (!dwTickNow)
  1124. {
  1125. lstrcpy(szTemplate, TEXT("[%lu] "));
  1126. dwTickLast = GetTickCount();
  1127. }
  1128. dwTickNow = GetTickCount();
  1129. wsprintf(ach, szTemplate, dwTickNow - dwTickLast);
  1130. dwTickLast = dwTickNow;
  1131. lstrcatW(ach, c_wszTrace);
  1132. #else
  1133. lstrcpyW(ach, c_wszTrace);
  1134. #endif
  1135. lstrcat(ach,wszBlank);
  1136. cch = lstrlenW(ach);
  1137. va_start(vArgs, pszMsg);
  1138. // (We convert the string, rather than simply input an
  1139. // LPCWSTR parameter, so the caller doesn't have to wrap
  1140. // all the string constants with the TEXT() macro.)
  1141. if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, ARRAYSIZE(wszBuf)))
  1142. {
  1143. wvsprintfW(&ach[cch], pwsz, vArgs);
  1144. UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
  1145. }
  1146. va_end(vArgs);
  1147. OutputDebugStringW(ach);
  1148. OutputDebugStringW(c_wszNewline);
  1149. }
  1150. SetLastError (dwLastError); // Restore the last error
  1151. }
  1152. /*----------------------------------------------------------
  1153. Purpose: Wide-char version of Dbg_SafeStrA
  1154. Returns: String ptr
  1155. Cond: --
  1156. */
  1157. LPCWSTR PUBLIC Dbg_SafeStrW(
  1158. LPCWSTR pwsz)
  1159. {
  1160. if (pwsz)
  1161. return pwsz;
  1162. else
  1163. return L"NULL";
  1164. }
  1165. #endif // WINNT
  1166. /*----------------------------------------------------------
  1167. Purpose: Assert failed message only
  1168. Returns: --
  1169. Cond: --
  1170. */
  1171. void CPUBLIC CommonAssertMsgA(
  1172. BOOL f,
  1173. LPCSTR pszMsg, ...)
  1174. {
  1175. CHAR ach[DEBUG_PRINT_BUFFER_LEN]; // Largest path plus extra
  1176. va_list vArgs;
  1177. if (!f)
  1178. {
  1179. int cch;
  1180. lstrcpyA(ach, c_szTrace);
  1181. cch = lstrlenA(ach);
  1182. va_start(vArgs, pszMsg);
  1183. wvsprintfA(&ach[cch], pszMsg, vArgs);
  1184. va_end(vArgs);
  1185. OutputDebugStringA(ach);
  1186. OutputDebugStringA(c_szNewline);
  1187. }
  1188. }
  1189. /*----------------------------------------------------------
  1190. Purpose: Debug spew
  1191. Returns: --
  1192. Cond: --
  1193. */
  1194. void CPUBLIC CommonDebugMsgA(
  1195. DWORD flag,
  1196. LPCSTR pszMsg, ...)
  1197. {
  1198. CHAR ach[DEBUG_PRINT_BUFFER_LEN]; // Largest path plus extra
  1199. va_list vArgs;
  1200. DWORD dwLastError = GetLastError ();
  1201. if (IsFlagSet(g_dwTraceFlags, flag))
  1202. {
  1203. int cch;
  1204. char szBlank[] = " ";
  1205. ASSERT( g_dwIndent >= 0);
  1206. szBlank[g_dwIndent < 0 ? 0 : g_dwIndent] = 0;
  1207. lstrcpyA(ach, c_szTrace);
  1208. lstrcatA(ach, szBlank);
  1209. cch = lstrlenA(ach);
  1210. va_start(vArgs, pszMsg);
  1211. wvsprintfA(&ach[cch], pszMsg, vArgs);
  1212. va_end(vArgs);
  1213. OutputDebugStringA(ach);
  1214. OutputDebugStringA(c_szNewline);
  1215. }
  1216. SetLastError (dwLastError);
  1217. }
  1218. #ifdef WANT_OLE_SUPPORT
  1219. /*----------------------------------------------------------
  1220. Purpose: Returns the string form of an known interface ID.
  1221. Returns: String ptr
  1222. Cond: --
  1223. */
  1224. LPCSTR PUBLIC Dbg_GetRiidName(
  1225. REFIID riid)
  1226. {
  1227. int i;
  1228. for (i = 0; i < ARRAYSIZE(c_rgriidmap); i++)
  1229. {
  1230. if (IsEqualIID(riid, c_rgriidmap[i].riid))
  1231. return c_rgriidmap[i].psz;
  1232. }
  1233. return "Unknown riid";
  1234. }
  1235. #endif
  1236. #ifdef __SCODE_H__
  1237. /*----------------------------------------------------------
  1238. Purpose: Returns the string form of an scode given an hresult.
  1239. Returns: String ptr
  1240. Cond: --
  1241. */
  1242. LPCSTR PUBLIC Dbg_GetScode(
  1243. HRESULT hres)
  1244. {
  1245. int i;
  1246. SCODE sc;
  1247. sc = GetScode(hres);
  1248. for (i = 0; i < ARRAYSIZE(c_rgscodemap); i++)
  1249. {
  1250. if (sc == c_rgscodemap[i].sc)
  1251. return c_rgscodemap[i].psz;
  1252. }
  1253. return "Unknown scode";
  1254. }
  1255. #endif // __SCODE_H__
  1256. /*----------------------------------------------------------
  1257. Purpose: Returns a string safe enough to print...and I don't
  1258. mean swear words.
  1259. Returns: String ptr
  1260. Cond: --
  1261. */
  1262. LPCSTR PUBLIC Dbg_SafeStrA(
  1263. LPCSTR psz)
  1264. {
  1265. if (psz)
  1266. return psz;
  1267. else
  1268. return "NULL";
  1269. }
  1270. #endif // !defined(NODEBUGHELP) && defined(DEBUG)
  1271. /*----------------------------------------------------------
  1272. Purpose: Entry-point to handle any necessary initialization
  1273. of the common data structures and functions.
  1274. Returns: TRUE on success
  1275. Cond: --
  1276. */
  1277. BOOL PUBLIC RovComm_Init(
  1278. HINSTANCE hinst)
  1279. {
  1280. BOOL bRet = TRUE;
  1281. #ifndef NODRAWTEXT
  1282. GetCommonMetrics(0);
  1283. #endif
  1284. bRet = RovComm_ProcessIniFile();
  1285. return bRet;
  1286. }
  1287. /*----------------------------------------------------------
  1288. Purpose: Entry-point to handle termination.
  1289. Returns: TRUE on success
  1290. Cond: --
  1291. */
  1292. BOOL PUBLIC RovComm_Terminate(
  1293. HINSTANCE hinst)
  1294. {
  1295. return TRUE;
  1296. }
  1297. /*----------------------------------------------------------
  1298. Purpose: Returns TRUE iff user has admin priveleges
  1299. Returns: --
  1300. Cond: --
  1301. */
  1302. BOOL PUBLIC IsAdminUser(void)
  1303. {
  1304. HKEY hkey;
  1305. if(RegOpenKeyEx(HKEY_USERS, TEXT(".DEFAULT"), 0, KEY_WRITE, &hkey) == 0)
  1306. {
  1307. RegCloseKey(hkey);
  1308. return TRUE;
  1309. }
  1310. return FALSE;
  1311. }