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.

1088 lines
21 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. version.c
  5. Abstract:
  6. This file implements a set of enumeration routines to access
  7. version info in a Win32 binary.
  8. Author:
  9. Jim Schmidt (jimschm) 03-Dec-1997
  10. Revision History:
  11. <alias> <date> <comments>
  12. --*/
  13. #include "pch.h"
  14. #define DBG_ACTION "Action"
  15. //
  16. // Globals
  17. //
  18. PCSTR g_DefaultTranslationsA[] = {
  19. "04090000",
  20. "040904E4",
  21. "040904B0",
  22. NULL
  23. };
  24. PCWSTR g_DefaultTranslationsW[] = {
  25. L"04090000",
  26. L"040904E4",
  27. L"040904B0",
  28. NULL
  29. };
  30. //
  31. // Prototypes
  32. //
  33. PCSTR
  34. pEnumVersionValueCommonA (
  35. IN OUT PVERSION_STRUCTA VersionStruct
  36. );
  37. PCWSTR
  38. pEnumVersionValueCommonW (
  39. IN OUT PVERSION_STRUCTW VersionStruct
  40. );
  41. //
  42. // Implementation
  43. //
  44. BOOL
  45. CreateVersionStructA (
  46. OUT PVERSION_STRUCTA VersionStruct,
  47. IN PCSTR FileSpec
  48. )
  49. /*++
  50. Routine Description:
  51. CreateVersionStruct is called to load a version structure from a file
  52. and to obtain the fixed version stamp info that is language-independent.
  53. The caller must call DestroyVersionStruct after the VersionStruct is no
  54. longer needed.
  55. Arguments:
  56. VersionStruct - Receives the version stamp info to be used by other
  57. functions in this module
  58. FileSpec - Specifies the file to obtain version info from
  59. Return Value:
  60. TRUE if the routine was able to get version info, or FALSE if an
  61. error occurred.
  62. --*/
  63. {
  64. //
  65. // Init the struct
  66. //
  67. ZeroMemory (VersionStruct, sizeof (VERSION_STRUCTA));
  68. VersionStruct->FileSpec = FileSpec;
  69. //
  70. // Allocate enough memory for the version stamp
  71. //
  72. VersionStruct->Size = GetFileVersionInfoSizeA (
  73. (PSTR) FileSpec,
  74. &VersionStruct->Handle
  75. );
  76. if (!VersionStruct->Size) {
  77. DEBUGMSG ((DBG_WARNING, "File %s does not have version info", FileSpec));
  78. return FALSE;
  79. }
  80. //
  81. // fix for version info bug:
  82. // allocate both buffers at once; this way the first buffer will not point to invalid
  83. // memory when a reallocation occurs because of the second grow
  84. //
  85. VersionStruct->VersionBuffer = GrowBuffer (&VersionStruct->GrowBuf, VersionStruct->Size * 2);
  86. if (!VersionStruct->VersionBuffer) {
  87. return FALSE;
  88. }
  89. VersionStruct->StringBuffer = VersionStruct->GrowBuf.Buf + VersionStruct->Size;
  90. //
  91. // Now get the version info from the file
  92. //
  93. if (!GetFileVersionInfoA (
  94. (PSTR) FileSpec,
  95. VersionStruct->Handle,
  96. VersionStruct->Size,
  97. VersionStruct->VersionBuffer
  98. )) {
  99. DestroyVersionStructA (VersionStruct);
  100. return FALSE;
  101. }
  102. //
  103. // Extract the fixed info
  104. //
  105. VerQueryValueA (
  106. VersionStruct->VersionBuffer,
  107. "\\",
  108. &VersionStruct->FixedInfo,
  109. &VersionStruct->FixedInfoSize
  110. );
  111. return TRUE;
  112. }
  113. ULONGLONG
  114. VerGetFileVer (
  115. IN PVERSION_STRUCTA VersionStruct
  116. )
  117. {
  118. ULONGLONG result = 0;
  119. if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
  120. *((PDWORD) (&result)) = VersionStruct->FixedInfo->dwFileVersionLS;
  121. *(((PDWORD) (&result)) + 1) = VersionStruct->FixedInfo->dwFileVersionMS;
  122. }
  123. return result;
  124. }
  125. ULONGLONG
  126. VerGetProductVer (
  127. IN PVERSION_STRUCTA VersionStruct
  128. )
  129. {
  130. ULONGLONG result = 0;
  131. if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
  132. *((PDWORD) (&result)) = VersionStruct->FixedInfo->dwProductVersionLS;
  133. *(((PDWORD) (&result)) + 1) = VersionStruct->FixedInfo->dwProductVersionMS;
  134. }
  135. return result;
  136. }
  137. DWORD
  138. VerGetFileDateLo (
  139. IN PVERSION_STRUCTA VersionStruct
  140. )
  141. {
  142. if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
  143. return VersionStruct->FixedInfo->dwFileDateLS;
  144. }
  145. return 0;
  146. }
  147. DWORD
  148. VerGetFileDateHi (
  149. IN PVERSION_STRUCTA VersionStruct
  150. )
  151. {
  152. if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
  153. return VersionStruct->FixedInfo->dwFileDateMS;
  154. }
  155. return 0;
  156. }
  157. DWORD
  158. VerGetFileVerOs (
  159. IN PVERSION_STRUCTA VersionStruct
  160. )
  161. {
  162. if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
  163. return VersionStruct->FixedInfo->dwFileOS;
  164. }
  165. return 0;
  166. }
  167. DWORD
  168. VerGetFileVerType (
  169. IN PVERSION_STRUCTA VersionStruct
  170. )
  171. {
  172. if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
  173. return VersionStruct->FixedInfo->dwFileType;
  174. }
  175. return 0;
  176. }
  177. VOID
  178. DestroyVersionStructA (
  179. IN PVERSION_STRUCTA VersionStruct
  180. )
  181. /*++
  182. Routine Description:
  183. DestroyVersionStruct cleans up all memory allocated by the routines
  184. in this module.
  185. Arguments:
  186. VersionStruct - Specifies the structure to clean up
  187. Return Value:
  188. none
  189. --*/
  190. {
  191. //
  192. // Clean up all allocations made by any routine using
  193. // the VersionStruct
  194. //
  195. if (VersionStruct->GrowBuf.Buf) {
  196. FreeGrowBuffer (&VersionStruct->GrowBuf);
  197. }
  198. ZeroMemory (VersionStruct, sizeof (VERSION_STRUCTA));
  199. }
  200. PCSTR
  201. EnumFirstVersionTranslationA (
  202. IN OUT PVERSION_STRUCTA VersionStruct
  203. )
  204. /*++
  205. Routine Description:
  206. EnumFirstVersionTranslation returins the translation string needed
  207. to access the string table of a version stamp.
  208. Arguments:
  209. VersionStruct - Specifies the structure that has been initialized
  210. by InitializeVersionStruct.
  211. Return Value:
  212. A pointer to a string specifying the first translation, or
  213. NULL if no translations exist.
  214. --*/
  215. {
  216. UINT ArraySize;
  217. //
  218. // Query version block for array of code pages/languages
  219. //
  220. if (!VerQueryValueA (
  221. VersionStruct->VersionBuffer,
  222. "\\VarFileInfo\\Translation",
  223. &VersionStruct->Translations,
  224. &ArraySize
  225. )) {
  226. //
  227. // No translations are available
  228. //
  229. ArraySize = 0;
  230. }
  231. //
  232. // Return a pointer to the first translation
  233. //
  234. VersionStruct->CurrentDefaultTranslation = 0;
  235. VersionStruct->MaxTranslations = ArraySize / sizeof (TRANSLATION);
  236. VersionStruct->CurrentTranslation = 0;
  237. DEBUGMSG_IF ((
  238. VersionStruct->MaxTranslations == 0,
  239. DBG_WARNING,
  240. "File %s has no translations",
  241. VersionStruct->FileSpec
  242. ));
  243. return EnumNextVersionTranslationA (VersionStruct);
  244. }
  245. BOOL
  246. pIsDefaultTranslationA (
  247. IN PCSTR TranslationStr
  248. )
  249. /*++
  250. Routine Description:
  251. pIsDefaultTranslationA returns TRUE if the specified translation
  252. string is enumerated by default. This routine stops multiple
  253. enumeration of the same translation string.
  254. Arguments:
  255. TranslationStr - Specifies the translation string to test
  256. Return Value:
  257. TRUE if the translation string is the same as a default translation
  258. string, or FALSE if it is not.
  259. --*/
  260. {
  261. INT i;
  262. for (i = 0 ; g_DefaultTranslationsA[i] ; i++) {
  263. if (StringIMatchA (TranslationStr, g_DefaultTranslationsA[i])) {
  264. return TRUE;
  265. }
  266. }
  267. return FALSE;
  268. }
  269. PCSTR
  270. EnumNextVersionTranslationA (
  271. IN OUT PVERSION_STRUCTA VersionStruct
  272. )
  273. /*++
  274. Routine Description:
  275. EnumNextVersionTranslation continues the enumeration of translation
  276. strings, needed to access the string table in a version stamp.
  277. Arguments:
  278. VersionStruct - Specifies the same structure passed to
  279. EnumFirstVersionTranslation.
  280. Return Value:
  281. A pointer to a string specifying the next translation, or
  282. NULL if no additional translations exist.
  283. --*/
  284. {
  285. PTRANSLATION Translation;
  286. if (g_DefaultTranslationsA[VersionStruct->CurrentDefaultTranslation]) {
  287. //
  288. // Return default translations first
  289. //
  290. StringCopyA (
  291. VersionStruct->TranslationStr,
  292. g_DefaultTranslationsA[VersionStruct->CurrentDefaultTranslation]
  293. );
  294. VersionStruct->CurrentDefaultTranslation++;
  295. } else {
  296. do {
  297. //
  298. // Return NULL if all translations have been enumerated
  299. //
  300. if (VersionStruct->CurrentTranslation == VersionStruct->MaxTranslations) {
  301. return NULL;
  302. }
  303. //
  304. // Otherwise build translation string and return pointer to it
  305. //
  306. Translation = &VersionStruct->Translations[VersionStruct->CurrentTranslation];
  307. wsprintfA (
  308. VersionStruct->TranslationStr,
  309. "%04x%04x",
  310. Translation->CodePage,
  311. Translation->Language
  312. );
  313. VersionStruct->CurrentTranslation++;
  314. } while (pIsDefaultTranslationA (VersionStruct->TranslationStr));
  315. }
  316. return VersionStruct->TranslationStr;
  317. }
  318. PCSTR
  319. EnumFirstVersionValueA (
  320. IN OUT PVERSION_STRUCTA VersionStruct,
  321. IN PCSTR VersionField
  322. )
  323. /*++
  324. Routine Description:
  325. EnumFirstVersionValue returns the first value stored in a version
  326. stamp for a specific field. If the field does not exist, the
  327. function returns NULL.
  328. An enumeration of EnumFirstVersionValue/EnumNextVersionValue
  329. is used to list all localized strings for a field.
  330. Arguments:
  331. VersionStruct - Specifies the structure that was initialized by
  332. InitializeVersionStruct.
  333. VersionField - Specifies the name of the version field to enumerate
  334. Return Value:
  335. A pointer to the first value of the field, or NULL if the field does
  336. not exist.
  337. --*/
  338. {
  339. PCSTR rc;
  340. if (!EnumFirstVersionTranslationA (VersionStruct)) {
  341. return NULL;
  342. }
  343. VersionStruct->VersionField = VersionField;
  344. rc = pEnumVersionValueCommonA (VersionStruct);
  345. if (!rc) {
  346. rc = EnumNextVersionValueA (VersionStruct);
  347. }
  348. return rc;
  349. }
  350. PCSTR
  351. EnumNextVersionValueA (
  352. IN OUT PVERSION_STRUCTA VersionStruct
  353. )
  354. /*++
  355. Routine Description:
  356. EnumNextVersionValue returns the next value stored in a version
  357. stamp for a specific field.
  358. Arguments:
  359. VersionStruct - Specifies the same structure passed to EnumFirstVersionField
  360. Return Value:
  361. A pointer to the next value of the field, or NULL if another field
  362. does not exist.
  363. --*/
  364. {
  365. PCSTR rc = NULL;
  366. do {
  367. if (!EnumNextVersionTranslationA (VersionStruct)) {
  368. break;
  369. }
  370. rc = pEnumVersionValueCommonA (VersionStruct);
  371. } while (!rc);
  372. return rc;
  373. }
  374. PCSTR
  375. pEnumVersionValueCommonA (
  376. IN OUT PVERSION_STRUCTA VersionStruct
  377. )
  378. /*++
  379. Routine Description:
  380. pEnumVersionValueCommon is a routine that obtains the value
  381. of a version field. It is used for both EnumFirstVersionValue
  382. and EnumNextVersionValue.
  383. Arguments:
  384. VersionStruct - Specifies the structure being processed
  385. Return Value:
  386. A pointer to the version value for the current translation, or
  387. NULL if the value does not exist for the current translation.
  388. --*/
  389. {
  390. PSTR Text;
  391. UINT StringLen;
  392. PBYTE String;
  393. PCSTR Result = NULL;
  394. //
  395. // Prepare sub block for VerQueryValue API
  396. //
  397. Text = AllocTextA (
  398. 16 +
  399. SizeOfStringA (VersionStruct->TranslationStr) +
  400. SizeOfStringA (VersionStruct->VersionField)
  401. );
  402. if (!Text) {
  403. return NULL;
  404. }
  405. wsprintfA (
  406. Text,
  407. "\\StringFileInfo\\%s\\%s",
  408. VersionStruct->TranslationStr,
  409. VersionStruct->VersionField
  410. );
  411. __try {
  412. //
  413. // Get the value from the version stamp
  414. //
  415. if (!VerQueryValueA (
  416. VersionStruct->VersionBuffer,
  417. Text,
  418. &String,
  419. &StringLen
  420. )) {
  421. //
  422. // No value is available
  423. //
  424. __leave;
  425. }
  426. //
  427. // Copy value into buffer
  428. //
  429. _mbsnzcpy (VersionStruct->StringBuffer, (PCSTR) String, StringLen);
  430. Result = VersionStruct->StringBuffer;
  431. }
  432. __finally {
  433. FreeTextA (Text);
  434. }
  435. return Result;
  436. }
  437. BOOL
  438. CreateVersionStructW (
  439. OUT PVERSION_STRUCTW VersionStruct,
  440. IN PCWSTR FileSpec
  441. )
  442. /*++
  443. Routine Description:
  444. CreateVersionStruct is called to load a version structure from a file
  445. and to obtain the fixed version stamp info that is language-independent.
  446. The caller must call DestroyVersionStruct after the VersionStruct is no
  447. longer needed.
  448. Arguments:
  449. VersionStruct - Receives the version stamp info to be used by other
  450. functions in this module
  451. FileSpec - Specifies the file to obtain version info from
  452. Return Value:
  453. TRUE if the routine was able to get version info, or FALSE if an
  454. error occurred.
  455. --*/
  456. {
  457. ZeroMemory (VersionStruct, sizeof (VERSION_STRUCTW));
  458. VersionStruct->FileSpec = FileSpec;
  459. //
  460. // Allocate enough memory for the version stamp
  461. //
  462. VersionStruct->Size = GetFileVersionInfoSizeW (
  463. (PWSTR) FileSpec,
  464. &VersionStruct->Handle
  465. );
  466. if (!VersionStruct->Size) {
  467. DEBUGMSG ((DBG_WARNING, "File %S does not have version info", FileSpec));
  468. return FALSE;
  469. }
  470. //
  471. // fix for version info bug:
  472. // allocate both buffers at once; this way the first buffer will not point to invalid
  473. // memory when a reallocation occurs because of the second grow
  474. //
  475. VersionStruct->VersionBuffer = GrowBuffer (&VersionStruct->GrowBuf, VersionStruct->Size * 2);
  476. if (!VersionStruct->VersionBuffer) {
  477. return FALSE;
  478. }
  479. VersionStruct->StringBuffer = VersionStruct->GrowBuf.Buf + VersionStruct->Size;
  480. //
  481. // Now get the version info from the file
  482. //
  483. if (!GetFileVersionInfoW (
  484. (PWSTR) FileSpec,
  485. VersionStruct->Handle,
  486. VersionStruct->Size,
  487. VersionStruct->VersionBuffer
  488. )) {
  489. DestroyVersionStructW (VersionStruct);
  490. return FALSE;
  491. }
  492. //
  493. // Extract the fixed info
  494. //
  495. VerQueryValueW (
  496. VersionStruct->VersionBuffer,
  497. L"\\",
  498. &VersionStruct->FixedInfo,
  499. &VersionStruct->FixedInfoSize
  500. );
  501. return TRUE;
  502. }
  503. VOID
  504. DestroyVersionStructW (
  505. IN PVERSION_STRUCTW VersionStruct
  506. )
  507. /*++
  508. Routine Description:
  509. DestroyVersionStruct cleans up all memory allocated by the routines
  510. in this module.
  511. Arguments:
  512. VersionStruct - Specifies the structure to clean up
  513. Return Value:
  514. none
  515. --*/
  516. {
  517. if (VersionStruct->GrowBuf.Buf) {
  518. FreeGrowBuffer (&VersionStruct->GrowBuf);
  519. }
  520. ZeroMemory (VersionStruct, sizeof (VERSION_STRUCTW));
  521. }
  522. PCWSTR
  523. EnumFirstVersionTranslationW (
  524. IN OUT PVERSION_STRUCTW VersionStruct
  525. )
  526. /*++
  527. Routine Description:
  528. EnumFirstVersionTranslation returins the translation string needed
  529. to access the string table of a version stamp.
  530. Arguments:
  531. VersionStruct - Specifies the structure that has been initialized
  532. by InitializeVersionStruct.
  533. Return Value:
  534. A pointer to a string specifying the first translation, or
  535. NULL if no translations exist.
  536. --*/
  537. {
  538. UINT ArraySize;
  539. if (!VerQueryValueW (
  540. VersionStruct->VersionBuffer,
  541. L"\\VarFileInfo\\Translation",
  542. &VersionStruct->Translations,
  543. &ArraySize
  544. )) {
  545. //
  546. // No translations are available
  547. //
  548. ArraySize = 0;
  549. }
  550. //
  551. // Return a pointer to the first translation
  552. //
  553. VersionStruct->CurrentDefaultTranslation = 0;
  554. VersionStruct->MaxTranslations = ArraySize / sizeof (TRANSLATION);
  555. VersionStruct->CurrentTranslation = 0;
  556. DEBUGMSG_IF ((
  557. VersionStruct->MaxTranslations == 0,
  558. DBG_WARNING,
  559. "File %S has no translations",
  560. VersionStruct->FileSpec
  561. ));
  562. return EnumNextVersionTranslationW (VersionStruct);
  563. }
  564. BOOL
  565. pIsDefaultTranslationW (
  566. IN PCWSTR TranslationStr
  567. )
  568. {
  569. INT i;
  570. for (i = 0 ; g_DefaultTranslationsW[i] ; i++) {
  571. if (StringIMatchW (TranslationStr, g_DefaultTranslationsW[i])) {
  572. return TRUE;
  573. }
  574. }
  575. return FALSE;
  576. }
  577. PCWSTR
  578. EnumNextVersionTranslationW (
  579. IN OUT PVERSION_STRUCTW VersionStruct
  580. )
  581. /*++
  582. Routine Description:
  583. EnumNextVersionTranslation continues the enumeration of translation
  584. strings, needed to access the string table in a version stamp.
  585. Arguments:
  586. VersionStruct - Specifies the same structure passed to
  587. EnumFirstVersionTranslation.
  588. Return Value:
  589. A pointer to a string specifying the next translation, or
  590. NULL if no additional translations exist.
  591. --*/
  592. {
  593. PTRANSLATION Translation;
  594. if (g_DefaultTranslationsW[VersionStruct->CurrentDefaultTranslation]) {
  595. StringCopyW (
  596. VersionStruct->TranslationStr,
  597. g_DefaultTranslationsW[VersionStruct->CurrentDefaultTranslation]
  598. );
  599. VersionStruct->CurrentDefaultTranslation++;
  600. } else {
  601. do {
  602. if (VersionStruct->CurrentTranslation == VersionStruct->MaxTranslations) {
  603. return NULL;
  604. }
  605. Translation = &VersionStruct->Translations[VersionStruct->CurrentTranslation];
  606. wsprintfW (
  607. VersionStruct->TranslationStr,
  608. L"%04x%04x",
  609. Translation->CodePage,
  610. Translation->Language
  611. );
  612. VersionStruct->CurrentTranslation++;
  613. } while (pIsDefaultTranslationW (VersionStruct->TranslationStr));
  614. }
  615. return VersionStruct->TranslationStr;
  616. }
  617. PCWSTR
  618. EnumFirstVersionValueW (
  619. IN OUT PVERSION_STRUCTW VersionStruct,
  620. IN PCWSTR VersionField
  621. )
  622. /*++
  623. Routine Description:
  624. EnumFirstVersionValue returns the first value stored in a version
  625. stamp for a specific field. If the field does not exist, the
  626. function returns NULL.
  627. An enumeration of EnumFirstVersionValue/EnumNextVersionValue
  628. is used to list all localized strings for a field.
  629. Arguments:
  630. VersionStruct - Specifies the structure that was initialized by
  631. InitializeVersionStruct.
  632. VersionField - Specifies the name of the version field to enumerate
  633. Return Value:
  634. A pointer to the first value of the field, or NULL if the field does
  635. not exist.
  636. --*/
  637. {
  638. PCWSTR rc;
  639. if (!EnumFirstVersionTranslationW (VersionStruct)) {
  640. return NULL;
  641. }
  642. VersionStruct->VersionField = VersionField;
  643. rc = pEnumVersionValueCommonW (VersionStruct);
  644. if (!rc) {
  645. rc = EnumNextVersionValueW (VersionStruct);
  646. }
  647. return rc;
  648. }
  649. PCWSTR
  650. EnumNextVersionValueW (
  651. IN OUT PVERSION_STRUCTW VersionStruct
  652. )
  653. /*++
  654. Routine Description:
  655. EnumNextVersionValue returns the next value stored in a version
  656. stamp for a specific field.
  657. Arguments:
  658. VersionStruct - Specifies the same structure passed to EnumFirstVersionField
  659. Return Value:
  660. A pointer to the next value of the field, or NULL if another field
  661. does not exist.
  662. --*/
  663. {
  664. PCWSTR rc = NULL;
  665. do {
  666. if (!EnumNextVersionTranslationW (VersionStruct)) {
  667. break;
  668. }
  669. rc = pEnumVersionValueCommonW (VersionStruct);
  670. } while (!rc);
  671. return rc;
  672. }
  673. PCWSTR
  674. pEnumVersionValueCommonW (
  675. IN OUT PVERSION_STRUCTW VersionStruct
  676. )
  677. /*++
  678. Routine Description:
  679. pEnumVersionValueCommon is a routine that obtains the value
  680. of a version field. It is used for both EnumFirstVersionValue
  681. and EnumNextVersionValue.
  682. Arguments:
  683. VersionStruct - Specifies the structure being processed
  684. Return Value:
  685. A pointer to the version value for the current translation, or
  686. NULL if the value does not exist for the current translation.
  687. --*/
  688. {
  689. PWSTR Text;
  690. UINT StringLen;
  691. PBYTE String;
  692. PCWSTR Result = NULL;
  693. //
  694. // Prepare sub block for VerQueryValue API
  695. //
  696. Text = AllocTextW (
  697. 18 +
  698. CharCountW (VersionStruct->TranslationStr) +
  699. CharCountW (VersionStruct->VersionField)
  700. );
  701. if (!Text) {
  702. return NULL;
  703. }
  704. wsprintfW (
  705. Text,
  706. L"\\StringFileInfo\\%s\\%s",
  707. VersionStruct->TranslationStr,
  708. VersionStruct->VersionField
  709. );
  710. __try {
  711. //
  712. // Get the value from the version stamp
  713. //
  714. if (!VerQueryValueW (
  715. VersionStruct->VersionBuffer,
  716. Text,
  717. &String,
  718. &StringLen
  719. )) {
  720. //
  721. // No value is available
  722. //
  723. return NULL;
  724. }
  725. CopyMemory (VersionStruct->StringBuffer, String, StringLen * sizeof (WCHAR));
  726. VersionStruct->StringBuffer [StringLen * sizeof (WCHAR)] = 0;
  727. Result = (PWSTR) VersionStruct->StringBuffer;
  728. }
  729. __finally {
  730. FreeTextW (Text);
  731. }
  732. return Result;
  733. }
  734. PSTR
  735. UnicodeToCcs (
  736. PCWSTR Source
  737. )
  738. /*++
  739. Routine Description:
  740. UnicodeToCcs will walk the unicode string and convert it to ANSII by encoding all DBCS characters
  741. to hex values.
  742. Arguments:
  743. Source - the Unicode string
  744. Return Value:
  745. An encoded ANSII string.
  746. --*/
  747. {
  748. CHAR result [MEMDB_MAX];
  749. UINT srcIndex = 0;
  750. UINT destIndex = 0;
  751. while (Source [srcIndex]) {
  752. if ((Source [srcIndex] >=32) &&
  753. (Source [srcIndex] <=126)
  754. ) {
  755. result [destIndex] = (BYTE) Source [srcIndex];
  756. destIndex ++;
  757. }
  758. else {
  759. if ((destIndex == 0) ||
  760. (result [destIndex-1] != '*')
  761. ) {
  762. result [destIndex] = '*';
  763. destIndex ++;
  764. }
  765. }
  766. srcIndex ++;
  767. }
  768. result [destIndex] = 0;
  769. return DuplicatePathString (result, 0);
  770. }