RESUTIL.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:20k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /******************************************************************************
  2. *       This is a part of the Microsoft Source Code Samples. 
  3. *       Copyright (C) 1993-1997 Microsoft Corporation.
  4. *       All rights reserved. 
  5. *       This source code is only intended as a supplement to 
  6. *       Microsoft Development Tools and/or WinHelp documentation.
  7. *       See these sources for detailed information regarding the 
  8. *       Microsoft samples programs.
  9. ******************************************************************************/
  10. /****************************** Module Header *******************************
  11. * Module Name: resutil.c
  12. *
  13. * Contains utility functions for working with the Windows resource file.
  14. *
  15. * Functions:
  16. *   ParseDialogBoxHeader()
  17. *   ParseControlData()
  18. *   DWordAlign()
  19. *   DWordPad()
  20. *   ResourceType)
  21. *   ResourceName()
  22. *   ResourcePart2()
  23. *   ResourceSize()
  24. *   SkipResHeader()
  25. *   SkipSz()
  26. *   SkipDialogBoxHeader()
  27. *   SkipControlData()
  28. *   NameOrdCpy()
  29. *   NameOrdCmp()
  30. *   NameOrdLen()
  31. *   NameOrdDup()
  32. *   StrToNameOrd()
  33. *   WriteOrd()
  34. *   WriteResHeader()
  35. *   WriteSz()
  36. *
  37. ****************************************************************************/
  38. #include "dlgedit.h"
  39. #include "dlgfuncs.h"
  40. #include "dlgextrn.h"
  41. #include <string.h>
  42. #include <wchar.h>
  43. /************************************************************************
  44. * ParseDialogBoxHeader
  45. *
  46. *
  47. * Arguments:
  48. *   PDIALOGBOXHEADER pdbh - Points to dialog box header to parse.
  49. *   PDWORD pflStyle       - Receives the style.
  50. *   PDWORD pflExtStyle    - Receives the extended style.
  51. *   PINT pcdit            - Receives the number of controls in the dialog.
  52. *   PINT px               - Receives starting x location.
  53. *   PINT py               - Receives starting y location.
  54. *   PINT pcx              - Receives the width.
  55. *   PINT pcy              - Receives the height.
  56. *   LPTSTR *ppszMenuName  - Receives the menu name.
  57. *   LPTSTR *ppszClass     - Receives the class name.
  58. *   LPTSTR *ppszCaption   - Receives the caption.
  59. *   PINT pPointSize       - Receives the point size.
  60. *   LPTSTR *ppszFontName  - Receives the font name.
  61. *
  62. * Returns:
  63. *   A pointer to the first dialog item past the dialog template header.
  64. *
  65. ************************************************************************/
  66. PCONTROLDATA ParseDialogBoxHeader(
  67.     PDIALOGBOXHEADER pdbh,
  68.     PDWORD pflStyle,
  69.     PDWORD pflExtStyle,
  70.     PINT pcdit,
  71.     PINT px,
  72.     PINT py,
  73.     PINT pcx,
  74.     PINT pcy,
  75.     LPTSTR *ppszMenuName,
  76.     LPTSTR *ppszClass,
  77.     LPTSTR *ppszCaption,
  78.     PINT pPointSize,
  79.     LPTSTR *ppszFontName)
  80. {
  81.     BYTE UNALIGNED *pb;
  82.     *pflStyle = pdbh->lStyle;
  83.     *pflExtStyle = pdbh->lExtendedStyle;
  84.     *pcdit = pdbh->NumberOfItems;
  85.     *px = (SHORT)pdbh->x;
  86.     *py = (SHORT)pdbh->y;
  87.     *pcx = (SHORT)pdbh->cx;
  88.     *pcy = (SHORT)pdbh->cy;
  89.     pb = (PBYTE)pdbh + SIZEOF_DIALOGBOXHEADER;
  90.     *ppszMenuName = (LPTSTR)pb;
  91.     pb += NameOrdLen((LPTSTR)pb);
  92.     *ppszClass = (LPTSTR)pb;
  93.     pb += NameOrdLen((LPTSTR)pb);
  94.     *ppszCaption = (LPTSTR)pb;
  95.     pb += (lstrlen((LPTSTR)pb) + 1) * sizeof(TCHAR);
  96.     /*
  97.      * Does the template specify a font?
  98.      */
  99.     if (pdbh->lStyle & DS_SETFONT) {
  100.         *pPointSize = (SHORT)(*(PWORD)pb);
  101.         pb += sizeof(WORD);
  102.         *ppszFontName = (LPTSTR)pb;
  103.         pb += (lstrlen((LPTSTR)pb) + 1) * sizeof(TCHAR);
  104.     }
  105.     else {
  106.         *pPointSize = 0;
  107.         *ppszFontName = NULL;
  108.     }
  109.     DWordAlign((PBYTE *)&pb);
  110.     return (PCONTROLDATA)pb;
  111. }
  112. /************************************************************************
  113. * ParseControlData
  114. *
  115. *
  116. * Arguments:
  117. *   PCONTROLDATA pcd    - Points to the control data to parse.
  118. *   PDWORD pflStyle     - Receives the control style.
  119. *   PDWORD pflExtStyle  - Receives the extended control style.
  120. *   PINT px             - Receives starting x location.
  121. *   PINT py             - Receives starting y location.
  122. *   PINT pcx            - Receives the width.
  123. *   PINT pcy            - Receives the height.
  124. *   PINT pid            - Receives the control id.
  125. *   LPTSTR *ppszClass   - Receives the class name.
  126. *   LPTSTR *ppszText    - Receives the text.
  127. *
  128. * Returns:
  129. *   A pointer to the next dialog item past the given one.
  130. *
  131. ************************************************************************/
  132. PCONTROLDATA ParseControlData(
  133.     PCONTROLDATA pcd,
  134.     PDWORD pflStyle,
  135.     PDWORD pflExtStyle,
  136.     PINT px,
  137.     PINT py,
  138.     PINT pcx,
  139.     PINT pcy,
  140.     PINT pid,
  141.     LPTSTR *ppszClass,
  142.     LPTSTR *ppszText)
  143. {
  144.     BYTE UNALIGNED *pb;
  145.     *pflStyle = pcd->lStyle;
  146.     *pflExtStyle = pcd->lExtendedStyle;
  147.     *px = (SHORT)pcd->x;
  148.     *py = (SHORT)pcd->y;
  149.     *pcx = (SHORT)pcd->cx;
  150.     *pcy = (SHORT)pcd->cy;
  151.     *pid = (SHORT)pcd->wId;
  152.     pb = (PBYTE)pcd + SIZEOF_CONTROLDATA;
  153.     *ppszClass = (LPTSTR)pb;
  154.     pb += NameOrdLen((LPTSTR)pb);
  155.     *ppszText = (LPTSTR)pb;
  156.     pb += NameOrdLen((LPTSTR)pb);
  157.     /*
  158.      * Finally, skip the Create Struct Data.
  159.      * After this, pb will be pointing to the next control.
  160.      */
  161.     pb += *(PWORD)pb + sizeof(WORD);
  162.     DWordAlign((PBYTE *)&pb);
  163.     return (PCONTROLDATA)pb;
  164. }
  165. /************************************************************************
  166. * DWordAlign
  167. *
  168. * This function aligns the passed pointer to a DWORD boundary.
  169. *
  170. * Arguments:
  171. *   PBYTE *ppb - Points to the pointer to align.
  172. *
  173. ************************************************************************/
  174. VOID DWordAlign(
  175.     PBYTE *ppb)
  176. {
  177.     *ppb += (4 - (((WORD)(DWORD)*ppb) & 3)) % 4;
  178. }
  179. /************************************************************************
  180. * DWordPad
  181. *
  182. * This function aligns the passed pointer to a DWORD boundary, padding
  183. * with nulls as it goes.
  184. *
  185. * Arguments:
  186. *   PBYTE *ppb - Points to the pointer to align.
  187. *
  188. ************************************************************************/
  189. VOID DWordPad(
  190.     PBYTE *ppb)
  191. {
  192.     WORD cbytes;
  193.     cbytes = (WORD)((4 - (((WORD)(DWORD)*ppb) & 3)) % 4);
  194.     while (cbytes) {
  195.         *((*ppb)++) = 0;
  196.         cbytes--;
  197.     }
  198. }
  199. /************************************************************************
  200. * ResourceType
  201. *
  202. * This function returns a pointer to the type of the resource.
  203. * The type can be either a string or an ordinal.
  204. *
  205. * Arguments:
  206. *   PRES pRes - Points to the start of the resource.
  207. *
  208. * Returns:
  209. *     Pointer to the type of the resource.
  210. *
  211. ************************************************************************/
  212. LPTSTR ResourceType(
  213.     PRES pRes)
  214. {
  215.     /*
  216.      * Skip past the two size fields.
  217.      */
  218.     return (LPTSTR)((PBYTE)pRes + sizeof(DWORD) + sizeof(DWORD));
  219. }
  220. /************************************************************************
  221. * ResourceName
  222. *
  223. * This function returns a pointer to the name of the resource.
  224. * The name can be either a string or an ordinal.
  225. *
  226. * Arguments:
  227. *   PRES pRes - Points to the start of the resource.
  228. *
  229. * Returns:
  230. *     Pointer to the name of the resource.
  231. *
  232. ************************************************************************/
  233. LPTSTR ResourceName(
  234.     PRES pRes)
  235. {
  236.     PBYTE pb;
  237.     /*
  238.      * Skip past the two size fields.
  239.      */
  240.     pb = (PBYTE)pRes + sizeof(DWORD) + sizeof(DWORD);
  241.     /*
  242.      * Skip past the "Type" field to the name.
  243.      */
  244.     return (LPTSTR)SkipSz((LPTSTR)pb);
  245. }
  246. /************************************************************************
  247. * ResourcePart2
  248. *
  249. * This function returns a pointer to the second half of the resource
  250. * header.
  251. *
  252. * Arguments:
  253. *   PRES pRes - Points to the start of the resource.
  254. *
  255. * Returns:
  256. *   A pointer to the second part of the resource header.
  257. *
  258. ************************************************************************/
  259. PRES2 ResourcePart2(
  260.     PRES pRes)
  261. {
  262.     PBYTE pb;
  263.     /*
  264.      * Skip past the first part of the resource header.
  265.      */
  266.     pb = (PBYTE)pRes + sizeof(RES);
  267.     /*
  268.      * Skip past the "Type" field to the name.
  269.      */
  270.     pb = SkipSz((LPTSTR)pb);
  271.     /*
  272.      * Skip past the name field also.
  273.      */
  274.     pb = SkipSz((LPTSTR)pb);
  275.     DWordAlign(&pb);
  276.     return (PRES2)pb;
  277. }
  278. /************************************************************************
  279. * ResourceSize
  280. *
  281. * This returns the size of the given resource.
  282. *
  283. * Arguments:
  284. *   PRES pRes - Points to the start of the resource.
  285. *
  286. * Returns:
  287. *   Size of the resource, including the header.
  288. *
  289. ************************************************************************/
  290. DWORD ResourceSize(
  291.     PRES pRes)
  292. {
  293.     return pRes->HeaderSize + pRes->DataSize;
  294. }
  295. /************************************************************************
  296. * SkipResHeader
  297. *
  298. * This function returns a pointer to the start of the resource data,
  299. * just past it's header.
  300. *
  301. * Arguments:
  302. *   PRES pRes - Pointer to the resource.
  303. *
  304. ************************************************************************/
  305. PBYTE SkipResHeader(
  306.     PRES pRes)
  307. {
  308.     return (PBYTE)pRes + pRes->HeaderSize;
  309. }
  310. /************************************************************************
  311. * SkipSz
  312. *
  313. * This function skips past a string and returns a pointer to just
  314. * past it.  It detects if the string is really an ordinal and skips
  315. * past these also.
  316. *
  317. * Arguments:
  318. *   LPTSTR pNameOrd - Pointer to the string/ordinal.
  319. *
  320. ************************************************************************/
  321. PBYTE SkipSz(
  322.     LPTSTR pNameOrd)
  323. {
  324.     if (IsOrd(pNameOrd))
  325.         pNameOrd = (LPTSTR)((PBYTE)pNameOrd + sizeof(ORDINAL));
  326.     else
  327.         pNameOrd += lstrlen(pNameOrd) + 1;
  328.     return (PBYTE)pNameOrd;
  329. }
  330. /************************************************************************
  331. * SkipDialogBoxHeader
  332. *
  333. * This function skips past a dialog template structure and returns
  334. * a pointer to the first dialog item template just past it.
  335. *
  336. * Arguments:
  337. *   PDIALOGBOXHEADER pdbh - Points to the dialog box header.
  338. *
  339. * Returns:
  340. *   A pointer to the first dialog item control data in the resource,
  341. *   just past the dialog box header that was skipped.
  342. *
  343. ************************************************************************/
  344. PCONTROLDATA SkipDialogBoxHeader(
  345.     PDIALOGBOXHEADER pdbh)
  346. {
  347.     BYTE UNALIGNED *pb;
  348.     /*
  349.      * Skip the fixed portion.
  350.      */
  351.     pb = (PBYTE)pdbh + SIZEOF_DIALOGBOXHEADER;
  352.     /*
  353.      * Skip the menu.
  354.      */
  355.     pb += NameOrdLen((LPTSTR)pb);
  356.     /*
  357.      * Skip the class.
  358.      */
  359.     pb += NameOrdLen((LPTSTR)pb);
  360.     /*
  361.      * Skip the caption.
  362.      */
  363.     pb += (lstrlen((LPTSTR)pb) + 1) * sizeof(TCHAR);
  364.     /*
  365.      * Does the template specify a font?
  366.      */
  367.     if (pdbh->lStyle & DS_SETFONT) {
  368.         pb += sizeof(WORD);
  369.         pb += (lstrlen((LPTSTR)pb) + 1) * sizeof(TCHAR);
  370.     }
  371.     DWordAlign((PBYTE *)&pb);
  372.     return (PCONTROLDATA)pb;
  373. }
  374. /************************************************************************
  375. * SkipControlData
  376. *
  377. * This function skips past the given control data to the next control.
  378. *
  379. * Arguments:
  380. *   PCONTROLDATA pcd - Points to the control data structure to skip.
  381. *
  382. * Returns:
  383. *   A pointer to the next control data structure.
  384. *
  385. ************************************************************************/
  386. PCONTROLDATA SkipControlData(
  387.     PCONTROLDATA pcd)
  388. {
  389.     BYTE UNALIGNED *pb;
  390.     /*
  391.      * Skip the fixed portion.
  392.      */
  393.     pb = (PBYTE)pcd + SIZEOF_CONTROLDATA;
  394.     /*
  395.      * Skip the class.
  396.      */
  397.     pb += NameOrdLen((LPTSTR)pb);
  398.     /*
  399.      * Skip the text.
  400.      */
  401.     pb += NameOrdLen((LPTSTR)pb);
  402.     /*
  403.      * Finally, skip the Create Struct Data.
  404.      * After this, pb will be pointing to the next control.
  405.      */
  406.     pb += *(PWORD)pb + sizeof(WORD);
  407.     DWordAlign((PBYTE *)&pb);
  408.     return (PCONTROLDATA)pb;
  409. }
  410. /************************************************************************
  411. * NameOrdCpy
  412. *
  413. * This function copies a string or ordinal.  This function needs to be
  414. * used whenever a string could possibly be an ordinal.  It returns a
  415. * pointer to the first byte after the copied name/ordinal.
  416. *
  417. * Arguments:
  418. *   LPTSTR pNameOrdDest - The destination buffer.
  419. *   LPTSTR pNameOrdSrc  - The source string or ordinal.
  420. *
  421. ************************************************************************/
  422. PBYTE NameOrdCpy(
  423.     LPTSTR pNameOrdDest,
  424.     LPTSTR pNameOrdSrc)
  425. {
  426.     if (IsOrd(pNameOrdSrc)) {
  427.         memcpy((PBYTE)pNameOrdDest, (PBYTE)pNameOrdSrc, sizeof(ORDINAL));
  428.         return (PBYTE)pNameOrdDest + sizeof(ORDINAL);
  429.     }
  430.     else {
  431.         lstrcpy(pNameOrdDest, pNameOrdSrc);
  432.         return (PBYTE)(pNameOrdDest + (lstrlen(pNameOrdDest) + 1));
  433.     }
  434. }
  435. /************************************************************************
  436. * NameOrdCmp
  437. *
  438. * This function compares two strings or ordinals.  It returns a
  439. * zero if they are equal, or non-zero if they are not.  This
  440. * follows the convention of lstrcmp(), but the return should
  441. * not be relied upon to determine which is "greater" than
  442. * the other.
  443. *
  444. * Arguments:
  445. *   LPTSTR pNameOrd1 - The first string or ordinal.
  446. *   LPTSTR pNameOrd2 - The second string or ordinal.
  447. *
  448. ************************************************************************/
  449. INT NameOrdCmp(
  450.     LPTSTR pNameOrd1,
  451.     LPTSTR pNameOrd2)
  452. {
  453.     BOOL fIsOrd1;
  454.     BOOL fIsOrd2;
  455.     fIsOrd1 = IsOrd(pNameOrd1);
  456.     fIsOrd2 = IsOrd(pNameOrd2);
  457.     if (fIsOrd1 != fIsOrd2)
  458.         return 1;
  459.     if (fIsOrd1)
  460.         return memcmp((PBYTE)pNameOrd1, (PBYTE)pNameOrd2, sizeof(ORDINAL));
  461.     else
  462.         return lstrcmp(pNameOrd1, pNameOrd2);
  463. }
  464. /************************************************************************
  465. * NameOrdLen
  466. *
  467. * This function returns the length of a string or ordinal.
  468. * If the given name is a string, the length of the string
  469. * plus the terminating null is returned.  Otherwise,
  470. * the size of an ORDINAL structure is returned.
  471. *
  472. * The length returned is in bytes, not wide-chars.
  473. *
  474. * Arguments:
  475. *   LPTSTR pNameOrd - The string or ordinal.
  476. *
  477. ************************************************************************/
  478. INT NameOrdLen(
  479.     LPTSTR pNameOrd)
  480. {
  481.     if (IsOrd(pNameOrd))
  482.         return sizeof(ORDINAL);
  483.     else
  484.         return (lstrlen(pNameOrd) + 1) * sizeof(TCHAR);
  485. }
  486. /****************************************************************************
  487. * NameOrdDup
  488. *
  489. * This function allocates a copy of the given name or ordinal.
  490. *
  491. * Arguments:
  492. *   LPTSTR pNameOrd - The name or ordinal to duplicate.
  493. *
  494. * Returns a pointer to the new copy if successful, NULL if it fails.
  495. *
  496. ****************************************************************************/
  497. LPTSTR NameOrdDup(
  498.     LPTSTR pNameOrd)
  499. {
  500.     register INT iLen;
  501.     LPTSTR psz;
  502.     iLen = NameOrdLen(pNameOrd);
  503.     if (!(psz = (LPTSTR)MyAlloc(iLen)))
  504.         return NULL;
  505.     NameOrdCpy(psz, pNameOrd);
  506.     return psz;
  507. }
  508. /************************************************************************
  509. * StrToNameOrd
  510. *
  511. * This function takes the given string, determines if it is
  512. * all numeric and if so, converts it in place into an ordinal.
  513. * It is used to convert the string from an edit field for
  514. * a value that can be an ordinal, such as the dialog name or
  515. * an icon's text.
  516. *
  517. * Note that the pszNameOrd buffer must be large enough for an
  518. * ordinal in case the string gets converted to an ordinal.
  519. *
  520. * Arguments:
  521. *   LPTSTR pszNameOrd  - On input, contains the string to possibly
  522. *                        convert.  On output, it will contain the
  523. *                        original string or the string converted to
  524. *                        an ordinal.
  525. *   BOOL fDecOnly      - TRUE if hex values and negative values (the
  526. *                        string starts with a '-') are not allowed.
  527. *                        This flag prevents these types of strings
  528. *                        from being candidates for conversion to
  529. *                        ordinals.
  530. *
  531. ************************************************************************/
  532. VOID StrToNameOrd(
  533.     LPTSTR pszNameOrd,
  534.     BOOL fDecOnly)
  535. {
  536.     register INT i;
  537.     INT nOrd;
  538.     INT nLen;
  539.     /*
  540.      * Empty string?
  541.      */
  542.     if (!(*pszNameOrd))
  543.         return;
  544.     nLen = lstrlen(pszNameOrd);
  545.     /*
  546.      * Is a hex value ok and does this appear to be a hex value?
  547.      */
  548.     if (!fDecOnly && pszNameOrd[0] == CHAR_0 &&
  549.             (pszNameOrd[1] == CHAR_X || pszNameOrd[1] == CHAR_CAP_X)) {
  550.         for (i = 2; i < nLen; i++) {
  551.             if (!iswxdigit(pszNameOrd[i]))
  552.                 return;
  553.         }
  554.         nOrd =  axtoi(&pszNameOrd[2]);
  555.     }
  556.     else {
  557.         /*
  558.          * All characters must be numeric.  Negative numbers may
  559.          * or may not be allowed, based on the fDecOnly flag.
  560.          */
  561.         for (i = 0; i < nLen; i++) {
  562.             if (!iswdigit(pszNameOrd[i]) &&
  563.                     (fDecOnly || i != 0 || pszNameOrd[0] != CHAR_MINUS))
  564.                 return;
  565.         }
  566.         nOrd = awtoi(pszNameOrd);
  567.     }
  568.     /*
  569.      * Return the ordinal in the original buffer.
  570.      */
  571.     WriteOrd((PORDINAL)pszNameOrd, nOrd);
  572. }
  573. /************************************************************************
  574. * WriteOrd
  575. *
  576. * This function writes out the given ordinal to the specified
  577. * memory location.  It returns the first byte past the newly
  578. * written ordinal.
  579. *
  580. * Arguments:
  581. *   PORDINAL pMem   - Pointer to the location to write the ordinal.
  582. *   INT nOrdinalID  - Ordinal ID to write.
  583. *
  584. ************************************************************************/
  585. PBYTE WriteOrd(
  586.     PORDINAL pOrd,
  587.     INT nOrdinalID)
  588. {
  589.     pOrd->wReserved = 0xffff;
  590.     pOrd->wOrdID = (WORD)nOrdinalID;
  591.     return (PBYTE)pOrd + sizeof(ORDINAL);
  592. }
  593. /************************************************************************
  594. * WriteResHeader
  595. *
  596. * This function writes out a resource header to the memory location
  597. * specified.
  598. *
  599. * Arguments:
  600. *
  601. *    PRES pRes            - pointer to the resource 
  602. *    DWORD DataSize       - size of the data
  603. *    INT iResType         - resource type
  604. *    LPTSTR pszResName    - resource name
  605. *    WORD fResFlags       - resource flags
  606. *    WORD LanguageId      - language id
  607. *    DWORD DataVersion    - data version
  608. *    DWORD Version        - version
  609. *    DWORD Characteristics-characterstics
  610. *
  611. * Returns:
  612. *    number of bytes written
  613. *
  614. ************************************************************************/
  615. PBYTE WriteResHeader(
  616.     PRES pRes,
  617.     DWORD DataSize,
  618.     INT iResType,
  619.     LPTSTR pszResName,
  620.     WORD fResFlags,
  621.     WORD LanguageId,
  622.     DWORD DataVersion,
  623.     DWORD Version,
  624.     DWORD Characteristics)
  625. {
  626.     PBYTE pb;
  627.     PRES2 pRes2;
  628.     pb = (PBYTE)pRes + sizeof(RES);
  629.     pb = WriteOrd((PORDINAL)pb, iResType);
  630.     pb = NameOrdCpy((LPTSTR)pb, pszResName);
  631.     DWordPad(&pb);
  632.     pRes->DataSize = DataSize;
  633.     pRes->HeaderSize = (pb - (PBYTE)pRes) + sizeof(RES2);
  634.     pRes2 = (PRES2)pb;
  635.     pRes2->DataVersion = DataVersion;
  636.     pRes2->MemoryFlags = fResFlags;
  637.     pRes2->LanguageId = LanguageId;
  638.     pRes2->Version = Version;
  639.     pRes2->Characteristics = Characteristics;
  640.     return (PBYTE)pRes + pRes->HeaderSize;
  641. }
  642. /************************************************************************
  643. * WriteSz
  644. *
  645. * This function writes out the string given to the specified
  646. * memory location.  It returns the first byte past the newly
  647. * written string.
  648. *
  649. * Arguments:
  650. *   LPTSTR pszDest - Pointer to the location to write the string.
  651. *   LPTSTR pszSrc  - The string to write.
  652. *
  653. ************************************************************************/
  654. PBYTE WriteSz(
  655.     LPTSTR pszDest,
  656.     LPTSTR pszSrc)
  657. {
  658.     while (*pszSrc)
  659.         *pszDest++ = *pszSrc++;
  660.     *pszDest++ = CHAR_NULL;
  661.     return (PBYTE)pszDest;
  662. }