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.

1609 lines
37 KiB

  1. #include "setupp.h"
  2. #pragma hdrstop
  3. #define INTL_ANSWER_FILE L"intl.txt"
  4. #define INTL_LANG_SECTION L"regionalsettings"
  5. #define INTL_LOCALE L"userlocale"
  6. #define INTL_KEYBOARD L"inputlocale"
  7. // This turns on some debug spew that lists all the available locales, geo
  8. // locations, and keyboards.
  9. #define INTL_LIST_OPTIONS 0
  10. #define LANG_LIST_INCREMENT 10
  11. POOBE_LOCALE_INFO LanguageList = NULL;
  12. DWORD LanguageListSize;
  13. DWORD LanguageIndex;
  14. DWORD DefaultID;
  15. DWORD DefaultIndex;
  16. typedef struct tagPHONEENTRY {
  17. LPWSTR Country;
  18. LPWSTR TollFreeNumber;
  19. LPWSTR TollNumber;
  20. } PHONEENTRY, *PPHONEENTRY;
  21. PWSTR PhoneList = NULL;
  22. DWORD PhoneListSize;
  23. DWORD PhoneListLength;
  24. //
  25. // Boolean value indicating whether we're doing a subset of gui-mode setup.
  26. //
  27. BOOL OobeSetup = FALSE;
  28. //
  29. // We need a global variable for OCM, coresponding to the local variable in
  30. // InstallWindowsNT().
  31. //
  32. #ifdef _OCM
  33. PVOID g_OcManagerContext;
  34. #endif
  35. BOOL
  36. WINAPI
  37. SetupGetProductType(
  38. PWSTR Product,
  39. PDWORD pSkuFlags
  40. )
  41. {
  42. Product[0] = 0;
  43. *pSkuFlags = 0;
  44. return TRUE;
  45. }
  46. PID3_RESULT
  47. WINAPI
  48. SetupPidGen3(
  49. PWSTR Pid3,
  50. DWORD SkuFlags,
  51. PWSTR OemId,
  52. BOOL Batch,
  53. PWSTR Pid2,
  54. LPBYTE lpPid3,
  55. LPBOOL Compliance
  56. )
  57. {
  58. if ( !InitializePidVariables() ) {
  59. SetupDebugPrint( L"SETUP: InitializePidVariables failed" );
  60. return PID_INVALID;
  61. }
  62. if ( !SetPid30Variables( Pid3 ) ) {
  63. SetupDebugPrint( L"SETUP: SetPid30Variables failed" );
  64. return PID_INVALID;
  65. }
  66. if ( !ValidateAndSetPid30() ) {
  67. SetupDebugPrint( L"SETUP: ValidateAndSetPid30 failed" );
  68. return PID_INVALID;
  69. }
  70. if(!SetProductIdInRegistry()) {
  71. SetupDebugPrint( L"SETUP: SetProductIdInRegistry failed" );
  72. return PID_INVALID;
  73. }
  74. return PID_VALID;
  75. }
  76. BOOL
  77. WINAPI
  78. SetupGetValidEula(
  79. PCWSTR Eula,
  80. PWSTR Path
  81. )
  82. {
  83. return TRUE;
  84. }
  85. BOOL
  86. CheckLangListSize(
  87. DWORD StructSize
  88. )
  89. {
  90. PVOID NewList;
  91. //
  92. // Check to make sure the LanguageList has at least 1 unused element.
  93. // If not, make it bigger.
  94. //
  95. if ( LanguageIndex == LanguageListSize ) {
  96. LanguageListSize *= 2;
  97. NewList = MyRealloc(
  98. LanguageList,
  99. LanguageListSize * StructSize
  100. );
  101. if ( NewList ) {
  102. LanguageList = NewList;
  103. } else {
  104. return FALSE;
  105. }
  106. }
  107. return TRUE;
  108. }
  109. VOID
  110. WINAPI
  111. SetupDestroyLanguageList(
  112. IN POOBE_LOCALE_INFO LanguageList,
  113. IN DWORD Count
  114. )
  115. {
  116. DWORD i;
  117. if ( LanguageList ) {
  118. for ( i=0; i < Count; i++ ) {
  119. if ( LanguageList + i ) {
  120. MyFree( LanguageList[i].Name );
  121. }
  122. }
  123. MyFree( LanguageList );
  124. }
  125. }
  126. BOOL
  127. CALLBACK
  128. EnumLocalesProc(
  129. PWSTR pszLocale
  130. )
  131. {
  132. BOOL b;
  133. LCID Locale;
  134. TCHAR LanguageName[128];
  135. POOBE_LOCALE_INFO pLocaleInfo;
  136. Locale = wcstoul (pszLocale, NULL, 16);
  137. b = GetLocaleInfo (
  138. Locale,
  139. LOCALE_SLANGUAGE | LOCALE_NOUSEROVERRIDE,
  140. LanguageName,
  141. sizeof(LanguageName) / sizeof(TCHAR)
  142. );
  143. MYASSERT(b);
  144. if ( !b ) {
  145. return FALSE;
  146. }
  147. //
  148. // Add it to our global array
  149. //
  150. if ( !CheckLangListSize( sizeof(OOBE_LOCALE_INFO) ) ) {
  151. SetupDestroyLanguageList( LanguageList, LanguageIndex );
  152. LanguageList = NULL;
  153. return FALSE;
  154. }
  155. pLocaleInfo = (POOBE_LOCALE_INFO)LanguageList + LanguageIndex;
  156. pLocaleInfo->Name = MyMalloc( (lstrlen(LanguageName) + 1) * sizeof(TCHAR) );
  157. if ( !pLocaleInfo->Name ) {
  158. SetupDestroyLanguageList( LanguageList, LanguageIndex );
  159. LanguageList = NULL;
  160. return FALSE;
  161. }
  162. lstrcpy( pLocaleInfo->Name, LanguageName );
  163. pLocaleInfo->Id = Locale;
  164. pLocaleInfo->Installed = IsValidLocale(
  165. Locale,
  166. LCID_INSTALLED
  167. );
  168. if ( Locale == DefaultID ) {
  169. DefaultIndex = LanguageIndex;
  170. }
  171. LanguageIndex++;
  172. return TRUE;
  173. }
  174. int
  175. __cdecl
  176. LocaleCompare(
  177. const void *arg1,
  178. const void *arg2
  179. )
  180. {
  181. return lstrcmp(
  182. ((POOBE_LOCALE_INFO)arg1)->Name,
  183. ((POOBE_LOCALE_INFO)arg2)->Name
  184. );
  185. }
  186. BOOL
  187. WINAPI
  188. SetupGetLocaleOptions(
  189. IN DWORD OptionalDefault,
  190. OUT POOBE_LOCALE_INFO *ReturnList,
  191. OUT PDWORD Items,
  192. OUT PDWORD Default
  193. )
  194. {
  195. BOOL bReturn = FALSE;
  196. DWORD i;
  197. //
  198. // Init our global variables
  199. //
  200. ASSERT_HEAP_IS_VALID();
  201. *ReturnList = NULL;
  202. MYASSERT( LanguageList == NULL );
  203. LanguageListSize = LANG_LIST_INCREMENT;
  204. LanguageList = MyMalloc( LanguageListSize * sizeof(OOBE_LOCALE_INFO));
  205. if ( !LanguageList ) {
  206. goto exit;
  207. }
  208. LanguageIndex = 0;
  209. DefaultID = OptionalDefault ? OptionalDefault : GetUserDefaultLCID();
  210. DefaultIndex = 0;
  211. EnumSystemLocales ( EnumLocalesProc , LCID_INSTALLED );
  212. if ( LanguageList ) {
  213. // Success
  214. qsort(
  215. LanguageList,
  216. LanguageIndex,
  217. sizeof( OOBE_LOCALE_INFO ),
  218. LocaleCompare
  219. );
  220. for (i=0; i<LanguageIndex; i++) {
  221. if (LanguageList[i].Id == DefaultID) {
  222. DefaultIndex = i;
  223. break;
  224. }
  225. }
  226. #if INTL_LIST_OPTIONS
  227. for (i=0; i<LanguageIndex; i++) {
  228. SetupDebugPrint2( L"Setup: SetupGetLocaleOptions|%x|%s",
  229. LanguageList[i].Id,
  230. LanguageList[i].Name );
  231. }
  232. #endif
  233. *ReturnList = LanguageList;
  234. LanguageList = NULL;
  235. *Items = LanguageIndex;
  236. *Default = DefaultIndex;
  237. bReturn = TRUE;
  238. }
  239. exit:
  240. ASSERT_HEAP_IS_VALID();
  241. return bReturn;
  242. }
  243. BOOL
  244. CALLBACK
  245. EnumGeoInfoProc(
  246. GEOID GeoID
  247. )
  248. {
  249. TCHAR pData[128];
  250. POOBE_LOCALE_INFO pGeoInfo;
  251. //
  252. // Add it to our global array
  253. //
  254. if ( !CheckLangListSize( sizeof(OOBE_LOCALE_INFO) ) ) {
  255. SetupDestroyLanguageList( LanguageList, LanguageIndex );
  256. LanguageList = NULL;
  257. return FALSE;
  258. }
  259. if( !GetGeoInfo(
  260. GeoID,
  261. GEO_FRIENDLYNAME,
  262. pData,
  263. sizeof(pData) / sizeof(TCHAR),
  264. 0
  265. )) {
  266. // Skip this one.
  267. MYASSERT(0);
  268. return TRUE;
  269. }
  270. pGeoInfo = (POOBE_LOCALE_INFO)LanguageList + LanguageIndex;
  271. pGeoInfo->Name = MyMalloc( (lstrlen(pData) + 1) * sizeof(TCHAR) );
  272. if ( !pGeoInfo->Name ) {
  273. SetupDestroyLanguageList( LanguageList, LanguageIndex );
  274. LanguageList = NULL;
  275. return FALSE;
  276. }
  277. lstrcpy( pGeoInfo->Name, pData );
  278. pGeoInfo->Id = GeoID;
  279. pGeoInfo->Installed = TRUE;
  280. if ( GeoID == (GEOID)DefaultID ) {
  281. DefaultIndex = LanguageIndex;
  282. }
  283. LanguageIndex++;
  284. return TRUE;
  285. }
  286. BOOL
  287. WINAPI
  288. SetupGetGeoOptions(
  289. IN DWORD OptionalDefault,
  290. OUT POOBE_LOCALE_INFO *ReturnList,
  291. OUT PDWORD Items,
  292. OUT PDWORD Default
  293. )
  294. {
  295. BOOL bReturn = FALSE;
  296. DWORD i;
  297. //
  298. // Init our global variables
  299. //
  300. ASSERT_HEAP_IS_VALID();
  301. *ReturnList = NULL;
  302. MYASSERT( LanguageList == NULL );
  303. LanguageListSize = LANG_LIST_INCREMENT;
  304. LanguageList = MyMalloc( LanguageListSize * sizeof(OOBE_LOCALE_INFO));
  305. if ( !LanguageList ) {
  306. goto exit;
  307. }
  308. LanguageIndex = 0;
  309. DefaultID = OptionalDefault ? OptionalDefault : GetUserGeoID( GEOCLASS_NATION );
  310. DefaultIndex = 0;
  311. bReturn = EnumSystemGeoID(
  312. GEOCLASS_NATION,
  313. 0,
  314. EnumGeoInfoProc
  315. );
  316. MYASSERT(bReturn);
  317. if ( bReturn && LanguageList ) {
  318. // Success
  319. qsort(
  320. LanguageList,
  321. LanguageIndex,
  322. sizeof( OOBE_LOCALE_INFO ),
  323. LocaleCompare
  324. );
  325. for (i=0; i<LanguageIndex; i++) {
  326. if (LanguageList[i].Id == DefaultID) {
  327. DefaultIndex = i;
  328. break;
  329. }
  330. }
  331. #if INTL_LIST_OPTIONS
  332. for (i=0; i<LanguageIndex; i++) {
  333. SetupDebugPrint2( L"Setup: SetupGetGeoOptions|%d|%s",
  334. LanguageList[i].Id,
  335. LanguageList[i].Name );
  336. }
  337. #endif
  338. bReturn = TRUE;
  339. *ReturnList = LanguageList;
  340. LanguageList = NULL;
  341. *Items = LanguageIndex;
  342. *Default = DefaultIndex;
  343. }
  344. exit:
  345. ASSERT_HEAP_IS_VALID();
  346. return bReturn;
  347. }
  348. BOOL
  349. WINAPI
  350. SetupGetKeyboardOptions(
  351. IN DWORD OptionalDefault,
  352. OUT POOBE_LOCALE_INFO *ReturnList,
  353. OUT PDWORD Items,
  354. OUT PDWORD Default
  355. )
  356. {
  357. DWORD DefaultKeyboard;
  358. DWORD i;
  359. BOOL bReturn = FALSE;
  360. TCHAR pData[128];
  361. TCHAR Substitute[9];
  362. POOBE_LOCALE_INFO pLocaleInfo;
  363. DWORD rc;
  364. HKEY hLangKey = NULL;
  365. HKEY hLangSubKey = NULL;
  366. DWORD Index;
  367. TCHAR SubKeyName[9];
  368. DWORD SubKeyNameLength;
  369. DWORD DataSize;
  370. DWORD Type;
  371. //
  372. // Initialize our variables
  373. //
  374. ASSERT_HEAP_IS_VALID();
  375. *ReturnList = NULL;
  376. MYASSERT( LanguageList == NULL );
  377. LanguageListSize = LANG_LIST_INCREMENT;
  378. LanguageList = MyMalloc( LanguageListSize * sizeof(OOBE_LOCALE_INFO));
  379. if ( !LanguageList ) {
  380. goto exit;
  381. }
  382. LanguageIndex = 0;
  383. // DefaultIndex = -1;
  384. *Default = 0;
  385. if (OptionalDefault) {
  386. DefaultKeyboard = OptionalDefault;
  387. } else {
  388. //
  389. // Lookup default keyboard in the registry
  390. //
  391. rc = RegOpenKeyEx( HKEY_USERS,
  392. L".DEFAULT\\Keyboard Layout\\Preload",
  393. 0,
  394. KEY_READ,
  395. &hLangKey );
  396. if( rc != NO_ERROR ) {
  397. SetupDebugPrint1( L"Setup: SetupGetKeyboardOptions - RegOpenKeyEx(.DEFAULT\\Keyboard Layout\\Preload) failed (%d)", rc );
  398. MYASSERT(0);
  399. goto exit;
  400. }
  401. DataSize = sizeof(pData);
  402. rc = RegQueryValueEx(
  403. hLangKey,
  404. L"1",
  405. NULL,
  406. &Type,
  407. (LPBYTE)pData,
  408. &DataSize
  409. );
  410. RegCloseKey( hLangKey );
  411. hLangKey = NULL;
  412. if( rc != NO_ERROR ) {
  413. SetupDebugPrint1( L"Setup: SetupGetKeyboardOptions - RegQueryValueEx(1) failed (%d)", rc );
  414. MYASSERT(0);
  415. goto exit;
  416. }
  417. DefaultKeyboard = wcstoul( pData, NULL, 16 );
  418. //
  419. // Now we look in the Substitutes key to see whether there is a
  420. // substitute there.
  421. //
  422. if( RegOpenKeyEx( HKEY_USERS,
  423. L".DEFAULT\\Keyboard Layout\\Substitutes",
  424. 0,
  425. KEY_READ,
  426. &hLangKey ) == NO_ERROR) {
  427. DataSize = sizeof(Substitute);
  428. if( (RegQueryValueEx( hLangKey,
  429. pData,
  430. NULL,
  431. &Type,
  432. (LPBYTE)Substitute,
  433. &DataSize ) == NO_ERROR) &&
  434. (Type == REG_SZ)
  435. ) {
  436. DefaultKeyboard = wcstoul( Substitute, NULL, 16 );
  437. }
  438. RegCloseKey(hLangKey);
  439. hLangKey = NULL;
  440. }
  441. }
  442. rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  443. L"System\\CurrentControlSet\\Control\\Keyboard Layouts",
  444. 0,
  445. KEY_ENUMERATE_SUB_KEYS | KEY_READ,
  446. &hLangKey );
  447. if( rc != NO_ERROR ) {
  448. SetupDebugPrint1( L"Setup: SetupGetKeyboardOptions - RegOpenKeyEx(System\\CurrentControlSet\\Control\\Keyboard Layouts) failed (%d)", rc );
  449. goto exit;
  450. }
  451. for( Index = 0; ; Index++ ) {
  452. SubKeyNameLength = sizeof(SubKeyName) / sizeof(TCHAR);
  453. rc = RegEnumKeyEx( hLangKey,
  454. Index,
  455. SubKeyName,
  456. &SubKeyNameLength,
  457. NULL,
  458. NULL,
  459. NULL,
  460. NULL );
  461. //
  462. // Did we error?
  463. //
  464. if( rc != ERROR_SUCCESS ) {
  465. //
  466. // Are we done?
  467. //
  468. if( rc != ERROR_NO_MORE_ITEMS ) {
  469. SetupDebugPrint2( L"Setup: SetupGetKeyboardOptions - RegEnumKeyEx failed (%d). Index = %d", rc, Index );
  470. MYASSERT(0);
  471. }
  472. break;
  473. }
  474. rc = RegOpenKeyEx( hLangKey,
  475. SubKeyName,
  476. 0,
  477. KEY_READ,
  478. &hLangSubKey );
  479. if( rc != NO_ERROR ) {
  480. SetupDebugPrint2( L"Setup: SetupGetKeyboardOptions - RegOpenKeyEx(%s) failed (%d)", SubKeyName, rc );
  481. MYASSERT(0);
  482. continue;
  483. }
  484. DataSize = sizeof(pData);
  485. rc = RegQueryValueEx(
  486. hLangSubKey,
  487. L"Layout Text",
  488. NULL,
  489. &Type,
  490. (LPBYTE)pData,
  491. &DataSize
  492. );
  493. RegCloseKey( hLangSubKey );
  494. hLangSubKey = NULL;
  495. if( rc != NO_ERROR ) {
  496. SetupDebugPrint2( L"Setup: SetupGetKeyboardOptions - RegQueryValueEx(Layout Text) for %s failed (%d)", SubKeyName, rc );
  497. MYASSERT(0);
  498. continue;
  499. }
  500. //
  501. // Add it to our global array
  502. //
  503. if ( !CheckLangListSize( sizeof(OOBE_LOCALE_INFO) ) ) {
  504. SetupDestroyLanguageList( LanguageList, LanguageIndex );
  505. LanguageList = NULL;
  506. goto exit;
  507. }
  508. pLocaleInfo = (POOBE_LOCALE_INFO)LanguageList + LanguageIndex;
  509. pLocaleInfo->Name = MyMalloc( (lstrlen(pData) + 1) * sizeof(TCHAR) );
  510. if ( !pLocaleInfo->Name ) {
  511. SetupDestroyLanguageList( LanguageList, LanguageIndex );
  512. LanguageList = NULL;
  513. goto exit;
  514. }
  515. lstrcpy( pLocaleInfo->Name, pData );
  516. pLocaleInfo->Id = wcstoul( SubKeyName, NULL, 16 );
  517. pLocaleInfo->Installed = TRUE;
  518. LanguageIndex++;
  519. }
  520. RegCloseKey( hLangKey );
  521. hLangKey = NULL;
  522. qsort(
  523. LanguageList,
  524. LanguageIndex,
  525. sizeof( OOBE_LOCALE_INFO ),
  526. LocaleCompare
  527. );
  528. for (i=0; i<LanguageIndex; i++) {
  529. if (LanguageList[i].Id == DefaultKeyboard) {
  530. *Default = i;
  531. break;
  532. }
  533. }
  534. #if INTL_LIST_OPTIONS
  535. for (i=0; i<LanguageIndex; i++) {
  536. SetupDebugPrint2( L"Setup: SetupGetKeyboardOptions|%x|%s",
  537. LanguageList[i].Id,
  538. LanguageList[i].Name );
  539. }
  540. #endif
  541. bReturn = TRUE;
  542. *ReturnList = LanguageList;
  543. LanguageList = NULL;
  544. *Items = LanguageIndex;
  545. exit:
  546. if ( hLangKey ) {
  547. RegCloseKey( hLangKey );
  548. }
  549. if ( hLangSubKey ) {
  550. RegCloseKey( hLangSubKey );
  551. }
  552. ASSERT_HEAP_IS_VALID();
  553. return bReturn;
  554. }
  555. BOOL
  556. WINAPI
  557. SetupSetIntlOptions(
  558. DWORD LocationIndex,
  559. DWORD LanguageIndex,
  560. DWORD KeyboardIndex
  561. )
  562. {
  563. WCHAR PathBuffer[MAX_PATH];
  564. WCHAR KeyValue[128];
  565. WCHAR CmdLine[MAX_PATH];
  566. BOOL bResult;
  567. SetupDebugPrint3(
  568. L"SetupSetIntlOptions: Location = %d, Language = 0x%08x, Keyboard = 0x%08x",
  569. LocationIndex,
  570. LanguageIndex,
  571. KeyboardIndex );
  572. GetSystemDirectory( PathBuffer, MAX_PATH );
  573. pSetupConcatenatePaths( PathBuffer, INTL_ANSWER_FILE, MAX_PATH, NULL );
  574. DeleteFile( PathBuffer );
  575. //
  576. // Write language value
  577. //
  578. wsprintf(
  579. KeyValue,
  580. L"\"%08x\"",
  581. LanguageIndex
  582. );
  583. WritePrivateProfileString(
  584. INTL_LANG_SECTION,
  585. INTL_LOCALE,
  586. KeyValue,
  587. PathBuffer
  588. );
  589. //
  590. // Write keyboard value
  591. //
  592. wsprintf(
  593. KeyValue,
  594. L"\"%04x:%08x\"",
  595. KeyboardIndex & 0x0000ffff,
  596. KeyboardIndex
  597. );
  598. WritePrivateProfileString(
  599. INTL_LANG_SECTION,
  600. INTL_KEYBOARD,
  601. KeyValue,
  602. PathBuffer
  603. );
  604. //
  605. // Call intl.cpl to do the work
  606. //
  607. wsprintf(
  608. CmdLine,
  609. L"/f:\"%s\" /s:\"%s\"",
  610. PathBuffer,
  611. LegacySourcePath
  612. );
  613. InvokeControlPanelApplet(L"intl.cpl",L"",0,CmdLine);
  614. DeleteFile( PathBuffer );
  615. //
  616. // Set the GEO location
  617. //
  618. bResult = SetUserGeoID( LocationIndex );
  619. if ( !bResult ) {
  620. SetupDebugPrint1(
  621. L"SetupSetIntlOptions: SetUserGeoID failed. Status = %d.",
  622. GetLastError()
  623. );
  624. }
  625. MYASSERT( bResult );
  626. return bResult;
  627. }
  628. //+---------------------------------------------------------------------------
  629. //
  630. // Function: CompareCntryNameLookUpElements()
  631. //
  632. // Synopsis: Function to compare names used by sort
  633. //
  634. //+---------------------------------------------------------------------------
  635. int __cdecl ComparePhoneEntry(const void *e1, const void *e2)
  636. {
  637. PPHONEENTRY pPhone1 = (PPHONEENTRY)e1;
  638. PPHONEENTRY pPhone2 = (PPHONEENTRY)e2;
  639. return CompareStringW(LOCALE_USER_DEFAULT, 0,
  640. pPhone1->Country, -1,
  641. pPhone2->Country, -1
  642. ) - 2;
  643. }
  644. VOID
  645. WINAPI
  646. SetupDestroyPhoneList(
  647. )
  648. {
  649. if ( PhoneList ) {
  650. MyFree( PhoneList );
  651. }
  652. PhoneList = NULL;
  653. }
  654. VOID
  655. WINAPI
  656. SetupFreePhoneList(PPHONEENTRY PhoneList, DWORD cbEntries)
  657. {
  658. DWORD i;
  659. if ( PhoneList )
  660. {
  661. for( i=0; i < cbEntries; i++ )
  662. {
  663. GlobalFree(PhoneList[i].Country);
  664. GlobalFree(PhoneList[i].TollFreeNumber);
  665. GlobalFree(PhoneList[i].TollNumber);
  666. }
  667. GlobalFree(PhoneList);
  668. }
  669. PhoneList = NULL;
  670. }
  671. BOOL
  672. AddToPhoneList(
  673. LPCWSTR Item
  674. )
  675. {
  676. PVOID NewList;
  677. DWORD ItemLength = lstrlen(Item);
  678. if ( !PhoneList ) {
  679. PhoneListLength = 0;
  680. PhoneListSize = 1024;
  681. PhoneList = MyMalloc( PhoneListSize * sizeof(TCHAR) );
  682. } else if ( PhoneListLength + ItemLength > PhoneListSize ) {
  683. PhoneListSize *= 2;
  684. NewList = MyRealloc(
  685. PhoneList,
  686. PhoneListSize * sizeof(TCHAR)
  687. );
  688. if ( NewList ) {
  689. PhoneList = NewList;
  690. } else {
  691. return FALSE;
  692. }
  693. }
  694. memcpy( PhoneList + PhoneListLength,
  695. Item,
  696. ItemLength * sizeof(TCHAR)
  697. );
  698. PhoneListLength += ItemLength;
  699. return TRUE;
  700. }
  701. BOOL MakePhoneListForScript(PPHONEENTRY PhoneList, DWORD cbEntries)
  702. {
  703. BOOL bRet = FALSE;
  704. DWORD i;
  705. if ( PhoneList )
  706. {
  707. for( i=0; i < cbEntries; i++ )
  708. {
  709. if (!AddToPhoneList(PhoneList[i].Country) ||
  710. !AddToPhoneList( TEXT("\t") ) ||
  711. !AddToPhoneList(PhoneList[i].TollFreeNumber) ||
  712. !AddToPhoneList( TEXT("\t") ) ||
  713. !AddToPhoneList(PhoneList[i].TollNumber) ||
  714. !AddToPhoneList( TEXT("\t") ) )
  715. {
  716. goto ExitMakePhoneListForScript;
  717. }
  718. }
  719. bRet = TRUE;
  720. }
  721. ExitMakePhoneListForScript:
  722. return bRet;
  723. }
  724. BOOL AddItemToPhoneEntry(LPCWSTR Item,
  725. LPWSTR *pPointer)
  726. {
  727. BOOL bRet = FALSE;
  728. if ((Item) && (pPointer))
  729. {
  730. *pPointer = (LPWSTR)GlobalAlloc(GPTR, (lstrlen(Item) + 1)*sizeof(TCHAR));
  731. if (*pPointer)
  732. {
  733. lstrcpy(*pPointer, Item);
  734. bRet = TRUE;
  735. }
  736. }
  737. return bRet;
  738. }
  739. PTSTR
  740. WINAPI
  741. SetupReadPhoneList(
  742. PWSTR PhoneInfName
  743. )
  744. {
  745. HINF PhoneInf = NULL;
  746. DWORD LineCount;
  747. DWORD ItemNo;
  748. LPCTSTR SectionName;
  749. INFCONTEXT InfContext;
  750. BOOL bSucceeded = FALSE;
  751. PPHONEENTRY pPhoneList = NULL;
  752. SetupDebugPrint( L"START: SetupReadPhoneList");
  753. PhoneList = NULL;
  754. PhoneInf = SetupOpenInfFile( PhoneInfName, NULL, INF_STYLE_WIN4, NULL );
  755. if( PhoneInf == INVALID_HANDLE_VALUE ) {
  756. return NULL;
  757. }
  758. SectionName = TEXT("IsoCodes");
  759. LineCount = SetupGetLineCount( PhoneInf, SectionName );
  760. if ( !LineCount ) {
  761. goto ReadPhoneListCleanup;
  762. }
  763. pPhoneList = (PPHONEENTRY)GlobalAlloc(GPTR,
  764. (int)(sizeof(PHONEENTRY) * LineCount));
  765. if (!pPhoneList )
  766. {
  767. goto ReadPhoneListCleanup;
  768. }
  769. for( ItemNo=0; ItemNo<LineCount; ItemNo++ ) {
  770. if( SetupGetLineByIndex( PhoneInf, SectionName, ItemNo, &InfContext )) {
  771. if ( !AddItemToPhoneEntry( pSetupGetField( &InfContext, 4 ), &pPhoneList[ItemNo].Country ) ) {
  772. goto ReadPhoneListCleanup;
  773. }
  774. if ( !AddItemToPhoneEntry( pSetupGetField( &InfContext, 5 ), &pPhoneList[ItemNo].TollFreeNumber ) ) {
  775. goto ReadPhoneListCleanup;
  776. }
  777. if ( !AddItemToPhoneEntry( pSetupGetField( &InfContext, 6 ), &pPhoneList[ItemNo].TollNumber ) ) {
  778. goto ReadPhoneListCleanup;
  779. }
  780. }
  781. }
  782. // sort the array
  783. qsort(pPhoneList, (int)LineCount,sizeof(PHONEENTRY),
  784. ComparePhoneEntry);
  785. // Convert the array into a TAB delimited list for script.
  786. if (MakePhoneListForScript(pPhoneList,LineCount))
  787. {
  788. //
  789. // Replace the final TAB with a NUL.
  790. //
  791. PhoneList[PhoneListLength-1] = '\0';
  792. bSucceeded = TRUE;
  793. }
  794. ReadPhoneListCleanup:
  795. if (pPhoneList)
  796. {
  797. SetupFreePhoneList(pPhoneList,LineCount);
  798. }
  799. SetupCloseInfFile( PhoneInf );
  800. SetupDebugPrint( L"END: SetupReadPhoneList");
  801. if ( bSucceeded ) {
  802. return PhoneList;
  803. } else {
  804. SetupDestroyPhoneList();
  805. return NULL;
  806. }
  807. }
  808. //
  809. // Read INF to map a TAPI country id to a 3 letter ISO code.
  810. //
  811. VOID
  812. SetupMapTapiToIso (
  813. IN PWSTR PhoneInfName,
  814. IN DWORD dwCountryID,
  815. OUT PWSTR szIsoCode
  816. )
  817. {
  818. HINF PhoneInf;
  819. WCHAR szCountryID[9];
  820. BOOL bResult;
  821. INFCONTEXT Context;
  822. szIsoCode[0] = L'\0';
  823. PhoneInf = SetupOpenInfFile( PhoneInfName, NULL, INF_STYLE_WIN4, NULL );
  824. if( PhoneInf == INVALID_HANDLE_VALUE ) {
  825. return;
  826. }
  827. wsprintf ( szCountryID, L"%d", dwCountryID);
  828. bResult = SetupFindFirstLine (
  829. PhoneInf,
  830. L"TapiCodes",
  831. szCountryID,
  832. &Context
  833. );
  834. if (bResult) {
  835. SetupGetStringField ( &Context, 1, szIsoCode, 4, NULL );
  836. SetupDebugPrint2 ( L"SetupMapTapiToIso: %d mapped to %s", dwCountryID, szIsoCode );
  837. }
  838. SetupCloseInfFile( PhoneInf );
  839. }
  840. BOOL
  841. WINAPI
  842. SetupGetSetupInfo(
  843. PWSTR Name, OPTIONAL
  844. DWORD cbName,
  845. PWSTR Org, OPTIONAL
  846. DWORD cbOrg,
  847. PWSTR OemId, OPTIONAL
  848. DWORD cbOemId,
  849. LPBOOL IntlSet OPTIONAL
  850. )
  851. {
  852. BOOL b = TRUE;
  853. HKEY hkey = NULL;
  854. DWORD Size;
  855. DWORD Type;
  856. //
  857. // Open the key if we need it
  858. //
  859. if( (Name || Org) &&
  860. RegOpenKeyEx(HKEY_LOCAL_MACHINE,WinntSoftwareKeyName,0,
  861. KEY_QUERY_VALUE,&hkey) != NO_ERROR) {
  862. return FALSE;
  863. }
  864. //
  865. // Get Name
  866. //
  867. if (Name) {
  868. Size = cbName;
  869. if((RegQueryValueEx(hkey,szRegisteredOwner,NULL,&Type,
  870. (LPBYTE)Name,&Size) != NO_ERROR)
  871. || (Type != REG_SZ)
  872. ) {
  873. b = FALSE;
  874. }
  875. }
  876. //
  877. // Get Org
  878. //
  879. if (Org) {
  880. Size = cbOrg;
  881. if((RegQueryValueEx(hkey,szRegisteredOrganization,NULL,&Type,
  882. (LPBYTE)Org,&Size) != NO_ERROR)
  883. || (Type != REG_SZ)
  884. ) {
  885. b = FALSE;
  886. }
  887. }
  888. // TBD: figure out what this is for
  889. if (OemId) {
  890. OemId[0] = 0;
  891. cbOemId = 0;
  892. }
  893. //
  894. // Note: IntlSet is not used currently
  895. //
  896. if (hkey) {
  897. RegCloseKey(hkey);
  898. }
  899. return b;
  900. }
  901. BOOL
  902. WINAPI
  903. SetupSetSetupInfo(
  904. PCWSTR Name,
  905. PCWSTR Org
  906. )
  907. {
  908. BOOL b;
  909. b = StoreNameOrgInRegistry( (PWSTR)Name, (PWSTR)Org );
  910. return b;
  911. }
  912. BOOL
  913. WINAPI
  914. SetupSetAdminPassword(
  915. PCWSTR OldPassword,
  916. PCWSTR NewPassword
  917. )
  918. {
  919. WCHAR AdminName[MAX_USERNAME+1];
  920. BOOL Status;
  921. GetAdminAccountName( AdminName );
  922. Status = SetLocalUserPassword( AdminName, OldPassword, NewPassword );
  923. if ( !Status ) {
  924. SetupDebugPrint( L"SetupSetAdminPassword: SetLocalUserPassword failed.");
  925. }
  926. return Status;
  927. }
  928. VOID
  929. WINAPI
  930. SetupOobeInitDebugLog(
  931. )
  932. {
  933. //
  934. // Do no UI. Note that we must set OobeSetup before our first call to
  935. // SetupDebugPrint.
  936. //
  937. OobeSetup = TRUE;
  938. SetupDebugPrint( L"SetupOobeInitDebugLog" );
  939. }
  940. // Run initialization that is known not to requires services to run.
  941. //
  942. VOID
  943. WINAPI
  944. SetupOobeInitPreServices(
  945. IN BOOL DoMiniSetupStuff
  946. )
  947. {
  948. //
  949. // Turn off logging.
  950. //
  951. // IsSetup = FALSE;
  952. SetupDebugPrint( L"SetupOobeInitPreServices" );
  953. if ( DoMiniSetupStuff ) {
  954. //
  955. // Act like the miniwizard (except with no UI)
  956. //
  957. MiniSetup = TRUE;
  958. Preinstall = TRUE;
  959. //
  960. // Tell SetupAPI not to bother backing up files and not to verify
  961. // that any INFs are digitally signed.
  962. //
  963. pSetupSetGlobalFlags(pSetupGetGlobalFlags()|PSPGF_NO_BACKUP|PSPGF_NO_VERIFY_INF);
  964. CommonInitialization();
  965. SetUpDataBlock();
  966. InternalSetupData.CallSpecificData1 = 0;
  967. #if 0
  968. //
  969. // We aren't going to do this for rev 1.
  970. //
  971. if( PnPReEnumeration ) {
  972. //
  973. // The user wants us to do PnP re-enumeration.
  974. // Go do it.
  975. //
  976. InstallPnpDevices( hdlg,
  977. SyssetupInf,
  978. GetDlgItem(hdlg,IDC_PROGRESS1),
  979. StartAtPercent,
  980. StopAtPercent );
  981. }
  982. #endif
  983. } else { // DoMiniSetupStuff
  984. //
  985. // Get handle to heap so we can periodically validate it.
  986. //
  987. #if DBG
  988. g_hSysSetupHeap = GetProcessHeap();
  989. #endif
  990. }
  991. }
  992. // Run initialization that may or does require services.
  993. //
  994. VOID
  995. WINAPI
  996. SetupOobeInitPostServices(
  997. IN BOOL DoMiniSetupStuff
  998. )
  999. {
  1000. InitializeExternalModules(
  1001. DoMiniSetupStuff,
  1002. &g_OcManagerContext
  1003. );
  1004. }
  1005. VOID
  1006. WINAPI
  1007. SetupOobeCleanup(
  1008. IN BOOL DoMiniSetupStuff
  1009. )
  1010. {
  1011. static FINISH_THREAD_PARAMS Context;
  1012. SetupDebugPrint( L"SetupOobeCleanup" );
  1013. if ( DoMiniSetupStuff ) {
  1014. RestoreBootTimeout();
  1015. Context.ThreadId = GetCurrentThreadId();
  1016. Context.OcManagerContext = g_OcManagerContext;
  1017. FinishThread( &Context );
  1018. }
  1019. }
  1020. // Resets the activation days (allowed 3 times only)
  1021. //
  1022. DWORD
  1023. SetupReArmWPA(
  1024. VOID
  1025. )
  1026. {
  1027. LPCSTR lpszReArmInterface = (LPCSTR)124;
  1028. typedef HRESULT (WINAPI* lpReArmEntryPoint) ();
  1029. HMODULE hRearmdll = NULL;
  1030. DWORD dwError = ERROR_SUCCESS;
  1031. hRearmdll = LoadLibraryA("licdll.dll");
  1032. if (hRearmdll)
  1033. {
  1034. lpReArmEntryPoint pReArmEntry =
  1035. (lpReArmEntryPoint) GetProcAddress(hRearmdll,lpszReArmInterface);
  1036. if (pReArmEntry)
  1037. {
  1038. //
  1039. // ValidateDigitalPid returns zero if success, otherwise its custom error code
  1040. //
  1041. HRESULT hr = (*pReArmEntry )();
  1042. if (FAILED(hr))
  1043. {
  1044. // If PID cannot be validated we should force activation/PID reentry.
  1045. SetupDebugPrint1(L"SETUP: Rollback WPA failed! HRESULT=%ld", hr);
  1046. dwError = (DWORD)hr;
  1047. }
  1048. else
  1049. SetupDebugPrint(L"SETUP: Rollback WPA succeeded.");
  1050. }
  1051. else {
  1052. SetupDebugPrint(L"SETUP: Failed to get WPA entry point!");
  1053. dwError = ERROR_INVALID_FUNCTION;
  1054. }
  1055. FreeLibrary (hRearmdll);
  1056. }
  1057. else {
  1058. SetupDebugPrint(L"SETUP: Failed to load WPA library!");
  1059. dwError = ERROR_FILE_NOT_FOUND;
  1060. }
  1061. // Return error code or success
  1062. //
  1063. return dwError;
  1064. }
  1065. // Once Windows is activated the Activate Windows shortcut is removed by msoobe.exe /a.
  1066. // If OEMs sysprep a machine they will need to re-activate Windows and the shortcut
  1067. // needs to be restored. Msoobe.exe cannot restore it because it does not
  1068. // run in server skus.
  1069. //
  1070. DWORD
  1071. SetupRestoreWPAShortcuts(
  1072. VOID
  1073. )
  1074. {
  1075. DWORD dwError = ERROR_SUCCESS;
  1076. HINF hinf;
  1077. hinf = SetupOpenInfFile(L"syssetup.inf",NULL,INF_STYLE_WIN4,NULL);
  1078. if(hinf != INVALID_HANDLE_VALUE)
  1079. {
  1080. if (SetupInstallFromInfSection(NULL,
  1081. hinf,
  1082. L"RESTORE_OOBE_ACTIVATE",
  1083. SPINST_PROFILEITEMS , //SPINST_ALL,
  1084. NULL,
  1085. NULL,
  1086. 0,
  1087. NULL,
  1088. NULL,
  1089. NULL,
  1090. NULL) != 0)
  1091. {
  1092. // Success
  1093. SetupDebugPrint(L"SETUP: Restore Activation shortcut succeeded");
  1094. }
  1095. else
  1096. {
  1097. // Failure
  1098. dwError = GetLastError();
  1099. SetupDebugPrint1(L"SETUP: Restore Activation shortcut failed. GetLastError=%ld",dwError);
  1100. }
  1101. SetupCloseInfFile(hinf);
  1102. }
  1103. else
  1104. {
  1105. dwError = GetLastError();
  1106. SetupDebugPrint1(L"SETUP: Restore Activation shortcut failed to open syssetup.inf. GetLastError=%ld",dwError);
  1107. }
  1108. return dwError;
  1109. }
  1110. BOOL Activationrequired(VOID);
  1111. // Rollback the activation days and put back the activate windows shortcut(s). X86 only.
  1112. //
  1113. DWORD
  1114. WINAPI
  1115. SetupOobeBnk(
  1116. LPBYTE pDummy
  1117. )
  1118. {
  1119. DWORD dwError = ERROR_SUCCESS;
  1120. // Return if we failed to rollback so we don'put the shortcut back
  1121. // On volume license skus this always returns successful
  1122. //
  1123. if (ERROR_SUCCESS != (dwError = SetupReArmWPA()))
  1124. return dwError;
  1125. // If not activated, restore the shortcuts, or if we don't
  1126. // require activation (for volume license skus)
  1127. //
  1128. if (Activationrequired())
  1129. {
  1130. // Restore the Activate Windows shortcut(s)
  1131. //
  1132. dwError = SetupRestoreWPAShortcuts();
  1133. }
  1134. return dwError;
  1135. }
  1136. static
  1137. LPTSTR
  1138. NextNumber(
  1139. LPTSTR lpString,
  1140. BOOL bSkipFirst
  1141. )
  1142. {
  1143. // The first time we just want to walk past any non-numbers,
  1144. // we don't want to skip any numbers if they are right at the
  1145. // begining of the string.
  1146. //
  1147. if ( bSkipFirst )
  1148. {
  1149. // Walk past the first number in the string.
  1150. //
  1151. while ( ( *lpString >= _T('0') ) &&
  1152. ( *lpString <= _T('9') ) )
  1153. {
  1154. lpString++;
  1155. }
  1156. }
  1157. // Now walk till we get to the next number or we reach the end.
  1158. //
  1159. while ( ( *lpString ) &&
  1160. ( ( *lpString < _T('0') ) ||
  1161. ( *lpString > _T('9') ) ) )
  1162. {
  1163. lpString++;
  1164. }
  1165. return lpString;
  1166. }
  1167. BOOL
  1168. WINAPI
  1169. SetupSetDisplay(
  1170. LPCTSTR lpszUnattend,
  1171. LPCTSTR lpszSection,
  1172. LPCTSTR lpszResolutionKey,
  1173. LPCTSTR lpszRefreshKey,
  1174. DWORD dwMinWidth,
  1175. DWORD dwMinHeight,
  1176. DWORD dwMinBits
  1177. )
  1178. {
  1179. DEVMODE devmode;
  1180. DWORD dwVal;
  1181. TCHAR szText[256];
  1182. LPTSTR lpDisplay;
  1183. BOOL bRet = TRUE;
  1184. ZeroMemory(&devmode, sizeof(DEVMODE));
  1185. devmode.dmSize = sizeof(DEVMODE);
  1186. // Check the current resolution, make sure it meets our mins.
  1187. //
  1188. if ( EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &devmode) )
  1189. {
  1190. if ( devmode.dmPelsWidth < dwMinWidth )
  1191. {
  1192. devmode.dmPelsWidth = dwMinWidth;
  1193. devmode.dmFields |= DM_PELSWIDTH;
  1194. }
  1195. if ( devmode.dmPelsHeight < dwMinHeight )
  1196. {
  1197. devmode.dmPelsHeight = dwMinHeight;
  1198. devmode.dmFields |= DM_PELSHEIGHT;
  1199. }
  1200. if ( devmode.dmBitsPerPel < dwMinBits )
  1201. {
  1202. devmode.dmBitsPerPel = dwMinBits;
  1203. devmode.dmFields |= DM_BITSPERPEL;
  1204. }
  1205. }
  1206. // Make sure they passed in an unattend and section to look in.
  1207. //
  1208. if ( lpszUnattend && *lpszUnattend && lpszSection && *lpszSection )
  1209. {
  1210. // Now check in the winbom to see if they want to change the current resolution.
  1211. //
  1212. szText[0] = _T('\0');
  1213. if ( ( lpszResolutionKey ) &&
  1214. ( *lpszResolutionKey ) &&
  1215. ( GetPrivateProfileString(lpszSection, lpszResolutionKey, _T(""), szText, sizeof(szText) / sizeof(szText[0]), lpszUnattend) ) &&
  1216. ( szText[0] ) )
  1217. {
  1218. bRet = FALSE;
  1219. lpDisplay = NextNumber(szText, FALSE);
  1220. if ( dwVal = (DWORD) _ttoi(lpDisplay) )
  1221. {
  1222. devmode.dmFields |= DM_PELSWIDTH;
  1223. devmode.dmPelsWidth = dwVal;
  1224. }
  1225. lpDisplay = NextNumber(lpDisplay, TRUE);
  1226. if ( dwVal = (DWORD) _ttoi(lpDisplay) )
  1227. {
  1228. devmode.dmFields |= DM_PELSHEIGHT;
  1229. devmode.dmPelsHeight = dwVal;
  1230. }
  1231. lpDisplay = NextNumber(lpDisplay, TRUE);
  1232. if ( dwVal = (DWORD) _ttoi(lpDisplay) )
  1233. {
  1234. devmode.dmFields |= DM_BITSPERPEL;
  1235. devmode.dmBitsPerPel = dwVal;
  1236. }
  1237. }
  1238. // Now check in the winbom to see if they want to change the default refresh rate.
  1239. //
  1240. szText[0] = _T('\0');
  1241. if ( ( lpszRefreshKey ) &&
  1242. ( *lpszRefreshKey ) &&
  1243. ( GetPrivateProfileString(lpszSection, lpszRefreshKey, _T(""), szText, sizeof(szText) / sizeof(szText[0]), lpszUnattend) ) &&
  1244. ( szText[0] ) )
  1245. {
  1246. bRet = FALSE;
  1247. if ( dwVal = (DWORD) _ttoi(szText) )
  1248. {
  1249. devmode.dmFields |= DM_DISPLAYFREQUENCY;
  1250. devmode.dmDisplayFrequency = dwVal;
  1251. }
  1252. }
  1253. }
  1254. // If we have anything to change, change it now.
  1255. //
  1256. if ( devmode.dmFields )
  1257. {
  1258. DWORD dwRet = ChangeDisplaySettings(&devmode, CDS_UPDATEREGISTRY | CDS_GLOBAL);
  1259. switch ( dwRet )
  1260. {
  1261. case DISP_CHANGE_SUCCESSFUL:
  1262. case DISP_CHANGE_RESTART:
  1263. bRet = TRUE;
  1264. break;
  1265. //case DISP_CHANGE_BADFLAGS:
  1266. //case DISP_CHANGE_BADPARAM:
  1267. //case DISP_CHANGE_FAILED:
  1268. //case DISP_CHANGE_BADMODE
  1269. //case DISP_CHANGE_NOTUPDATED:
  1270. //bRet = FALSE;
  1271. }
  1272. }
  1273. return bRet;
  1274. }
  1275. typedef struct _OEM_FINISH_APPS {
  1276. LPTSTR szApp;
  1277. LPTSTR szArgs;
  1278. } OEM_FINISH_APPS;
  1279. OEM_FINISH_APPS OEM_Finish_Apps[] = {
  1280. { L"Rundll32.exe", L"fldrclnr.dll,Wizard_RunDLL silent"},
  1281. { NULL, NULL} // End of list.
  1282. };
  1283. void RunOEMExtraTasks()
  1284. {
  1285. LPTSTR pApp = NULL;
  1286. LPTSTR pArgs = NULL;
  1287. DWORD dwSize;
  1288. DWORD dwCode;
  1289. int i;
  1290. BEGIN_SECTION(L"RunOEMExtraTasks");
  1291. i = 0;
  1292. while (OEM_Finish_Apps[i].szApp != NULL)
  1293. {
  1294. // Get the size we need to the expanded app
  1295. dwSize = ExpandEnvironmentStrings(
  1296. OEM_Finish_Apps[i].szApp ,
  1297. NULL,
  1298. 0);
  1299. if (dwSize)
  1300. {
  1301. pApp = (LPTSTR)GlobalAlloc(GPTR, sizeof(TCHAR) * dwSize);
  1302. if (pApp)
  1303. {
  1304. ExpandEnvironmentStrings(
  1305. OEM_Finish_Apps[i].szApp ,
  1306. pApp,
  1307. dwSize);
  1308. if (OEM_Finish_Apps[i].szArgs)
  1309. {
  1310. // Get the size we need to the expanded arguments
  1311. dwSize = ExpandEnvironmentStrings(
  1312. OEM_Finish_Apps[i].szArgs ,
  1313. NULL,
  1314. 0);
  1315. if (dwSize)
  1316. {
  1317. pArgs = (LPTSTR)GlobalAlloc(GPTR, sizeof(TCHAR) * dwSize);
  1318. if (pArgs)
  1319. {
  1320. ExpandEnvironmentStrings(
  1321. OEM_Finish_Apps[i].szArgs,
  1322. pArgs,
  1323. dwSize);
  1324. }
  1325. }
  1326. }
  1327. // Log what we will start
  1328. if (pArgs)
  1329. {
  1330. SetupDebugPrint2(L"Start command :%s: with arguments :%s:", pApp, pArgs);
  1331. }
  1332. else
  1333. {
  1334. SetupDebugPrint1(L"Start command :%s: with no arguments", pApp);
  1335. }
  1336. // Start the app.
  1337. dwCode = 0;
  1338. if (pArgs)
  1339. {
  1340. InvokeExternalApplicationEx(pApp, pArgs, &dwCode, INFINITE, TRUE);
  1341. }
  1342. else
  1343. {
  1344. // If we don't have args. the first parameter is NULL
  1345. InvokeExternalApplicationEx(NULL, pApp, &dwCode, INFINITE, TRUE);
  1346. }
  1347. }
  1348. }
  1349. if (pApp)
  1350. {
  1351. GlobalFree(pApp);
  1352. pApp = NULL;
  1353. }
  1354. if (pArgs)
  1355. {
  1356. GlobalFree(pArgs);
  1357. pArgs = NULL;
  1358. }
  1359. i++;
  1360. }
  1361. END_SECTION(L"RunOEMExtraTasks");
  1362. }