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.

684 lines
18 KiB

  1. /****************************************************************************
  2. Copyright (c) Microsoft Corporation 1997-1999
  3. All rights reserved
  4. ***************************************************************************/
  5. #include "pch.h"
  6. #include "dialogs.h"
  7. #include "check.h"
  8. #include "setup.h"
  9. #include "automate.h"
  10. DEFINE_MODULE("Main");
  11. // Globals
  12. HINSTANCE g_hinstance = NULL;
  13. OPTIONS g_Options;
  14. // Command line flags
  15. #define OPTION_UNKNOWN 0x00
  16. #define OPTION_VERSIONINGOVERRIDE 0x01
  17. #define OPTION_DEBUG 0x02
  18. #define OPTION_FUNC 0x03
  19. #define OPTION_CHECK 0x04
  20. #define OPTION_ADD 0x05
  21. #define OPTION_UPGRADE 0x06
  22. #define OPTION_AUTOMATED 0x08
  23. // Constants
  24. #define NUMBER_OF_PAGES 15
  25. //
  26. // Adds a page to the dialog.
  27. //
  28. void
  29. AddPage(
  30. LPPROPSHEETHEADER ppsh,
  31. UINT id,
  32. DLGPROC pfn,
  33. UINT idTitle,
  34. UINT idSubtitle )
  35. {
  36. PROPSHEETPAGE psp;
  37. TCHAR szTitle[ SMALL_BUFFER_SIZE ];
  38. TCHAR szSubTitle[ SMALL_BUFFER_SIZE ];
  39. ZeroMemory( &psp, sizeof(psp) );
  40. psp.dwSize = sizeof(psp);
  41. psp.dwFlags = PSP_DEFAULT | PSP_USETITLE;
  42. if ( id == IDD_WELCOME || id == IDD_WELCOME_ADD || id == IDD_WELCOME_CHECK )
  43. {
  44. psp.dwFlags |= PSP_HIDEHEADER;
  45. }
  46. else
  47. {
  48. psp.dwFlags |= PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
  49. if ( idTitle )
  50. {
  51. DWORD dw;
  52. dw = LoadString( g_hinstance, idTitle, szTitle, ARRAYSIZE(szTitle) );
  53. Assert( dw );
  54. psp.pszHeaderTitle = szTitle;
  55. }
  56. else
  57. {
  58. psp.pszHeaderTitle = NULL;
  59. }
  60. if ( idSubtitle )
  61. {
  62. DWORD dw;
  63. dw = LoadString( g_hinstance, idSubtitle , szSubTitle, ARRAYSIZE(szSubTitle) );
  64. Assert( dw );
  65. psp.pszHeaderSubTitle = szSubTitle;
  66. }
  67. else
  68. {
  69. psp.pszHeaderSubTitle = NULL;
  70. }
  71. }
  72. psp.pszTitle = g_Options.fCheckServer
  73. ? MAKEINTRESOURCE( IDS_CHECK_SERVER_TITLE)
  74. : MAKEINTRESOURCE( IDS_APPNAME );
  75. psp.hInstance = ppsh->hInstance;
  76. psp.pszTemplate = MAKEINTRESOURCE(id);
  77. psp.pfnDlgProc = pfn;
  78. ppsh->phpage[ ppsh->nPages ] = CreatePropertySheetPage( &psp );
  79. if ( ppsh->phpage[ ppsh->nPages ] )
  80. ppsh->nPages++;
  81. }
  82. //
  83. // Creates the UI pages and kicks off the property sheet.
  84. //
  85. HRESULT
  86. WizardPages( )
  87. {
  88. TraceFunc( "WizardPages( )\n" );
  89. HRESULT hr = S_OK;
  90. HPROPSHEETPAGE rPages[ NUMBER_OF_PAGES ];
  91. PROPSHEETHEADER pshead;
  92. ZeroMemory( &pshead, sizeof(pshead) );
  93. pshead.dwSize = sizeof(pshead);
  94. pshead.dwFlags = PSH_WIZARD97 | PSH_PROPTITLE | PSH_USEHICON
  95. | PSH_WATERMARK | PSH_HEADER;
  96. pshead.hInstance = g_hinstance;
  97. pshead.pszCaption = g_Options.fCheckServer
  98. ? MAKEINTRESOURCE( IDS_CHECK_SERVER_TITLE)
  99. : MAKEINTRESOURCE( IDS_APPNAME );
  100. pshead.phpage = rPages;
  101. pshead.pszbmWatermark = MAKEINTRESOURCE( IDB_TITLEPAGE );
  102. pshead.pszbmHeader = MAKEINTRESOURCE( IDB_HEADER );
  103. AddPage( &pshead, IDD_WELCOME, (DLGPROC) WelcomeDlgProc, 0, 0 );
  104. AddPage( &pshead, IDD_WELCOME_ADD, (DLGPROC) AddWelcomeDlgProc, 0, 0 );
  105. AddPage( &pshead, IDD_WELCOME_CHECK, (DLGPROC) CheckWelcomeDlgProc, 0, 0 );
  106. AddPage( &pshead, IDD_EXAMINING_SERVER, (DLGPROC) ExamineServerDlgProc, IDS_EXAMINING_TITLE, IDS_EXAMINING_SUBTITLE );
  107. AddPage( &pshead, IDD_INTELLIMIRRORROOT,(DLGPROC) IntelliMirrorRootDlgProc, IDS_INTELLIMIRRORROOT_TITLE, IDS_INTELLIMIRRORROOT_SUBTITLE );
  108. AddPage( &pshead, IDD_SCP, (DLGPROC) SCPDlgProc, IDS_SCP_TITLE, IDS_SCP_SUBTITLE );
  109. AddPage( &pshead, IDD_OPTIONS, (DLGPROC) OptionsDlgProc, IDS_OPTIONS_TITLE, IDS_OPTIONS_SUBTITLE );
  110. AddPage( &pshead, IDD_IMAGESOURCE, (DLGPROC) ImageSourceDlgProc, IDS_IMAGESOURCE_TITLE, IDS_IMAGESOURCE_SUBTITLE );
  111. AddPage( &pshead, IDD_LANGUAGE, (DLGPROC) LanguageDlgProc, IDS_LANGUAGE_TITLE, IDS_LANGUAGE_SUBTITLE );
  112. AddPage( &pshead, IDD_OSDIRECTORY, (DLGPROC) OSDirectoryDlgProc, IDS_OSDIRECTORY_TITLE, IDS_OSDIRECTORY_SUBTITLE );
  113. AddPage( &pshead, IDD_DEFAULTSIF, (DLGPROC) DefaultSIFDlgProc, IDS_DEFAULTSIF_TITLE, IDS_DEFAULTSIF_SUBTITLE );
  114. AddPage( &pshead, IDD_SCREENS, (DLGPROC) ScreensDlgProc, IDS_SCREENS_TITLE, IDS_SCREENS_SUBTITLE );
  115. AddPage( &pshead, IDD_SUMMARY, (DLGPROC) SummaryDlgProc, IDS_SUMMARY_TITLE, IDS_SUMMARY_SUBTITLE );
  116. AddPage( &pshead, IDD_WARNING, (DLGPROC) WarningDlgProc, IDS_WARNING_TITLE, IDS_WARNING_SUBTITLE );
  117. AddPage( &pshead, IDD_SERVEROK, (DLGPROC) ServerOKDlgProc, IDS_SERVEROK_TITLE, IDS_SERVEROK_SUBTITLE );
  118. PropertySheet( &pshead );
  119. if ( g_Options.fAbort )
  120. {
  121. hr = S_FALSE;
  122. goto Error;
  123. }
  124. if ( g_Options.fError )
  125. {
  126. hr = E_FAIL;
  127. goto Error;
  128. }
  129. Error:
  130. RETURN(hr);
  131. }
  132. //
  133. // Initializes g_Options.
  134. //
  135. HRESULT
  136. InitializeOptions( void )
  137. {
  138. DWORD dw;
  139. LRESULT lResult;
  140. HKEY hkeySetup;
  141. TraceFunc( "InitializeOptions( )\n" );
  142. //
  143. // Initialize all variable to NULL strings or FALSE
  144. //
  145. memset( &g_Options, 0, sizeof(OPTIONS) );
  146. //
  147. // Load default strings
  148. //
  149. dw = LoadString( g_hinstance, IDS_DEFAULTSETUP,
  150. g_Options.szInstallationName, ARRAYSIZE(g_Options.szInstallationName) );
  151. Assert( dw );
  152. dw = LoadString( g_hinstance, IDS_UNKNOWN, g_Options.szLanguage, ARRAYSIZE(g_Options.szLanguage) );
  153. Assert( dw );
  154. wcscpy( g_Options.szSourcePath, L"C:\\" );
  155. for( ; g_Options.szSourcePath[0] <= L'Z'; g_Options.szSourcePath[0]++ )
  156. {
  157. UINT uDriveType;
  158. uDriveType = GetDriveType( g_Options.szSourcePath );
  159. if ( DRIVE_CDROM == uDriveType )
  160. break;
  161. }
  162. if ( g_Options.szSourcePath[0] > L'Z' ) {
  163. g_Options.szSourcePath[0] = L'\0';
  164. }
  165. g_Options.hinf = INVALID_HANDLE_VALUE;
  166. g_Options.fFirstTime = TRUE;
  167. lResult = RegOpenKey( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Setup", &hkeySetup );
  168. if ( lResult == ERROR_SUCCESS )
  169. {
  170. DWORD dwValue;
  171. DWORD cbValue;
  172. // Find out if we should authorize DCHP
  173. cbValue = sizeof(dwValue);
  174. lResult = RegQueryValueEx( hkeySetup, L"RemInst_DontAuthorizeDHCP", NULL, NULL, (LPBYTE)&dwValue, &cbValue );
  175. if ( lResult == ERROR_SUCCESS ) {
  176. g_Options.fDontAuthorizeDhcp = dwValue;
  177. }
  178. RegCloseKey( hkeySetup );
  179. }
  180. if (SUCCEEDED(GetSetRanFlag(TRUE, FALSE))) {
  181. g_Options.fFirstTime = FALSE;
  182. } else {
  183. g_Options.fFirstTime = TRUE;
  184. }
  185. HRETURN(S_OK);
  186. }
  187. //
  188. // IsWhiteSpace()
  189. //
  190. BOOL
  191. IsWhiteSpace( wchar_t ch )
  192. {
  193. if ( ch <=32 )
  194. return TRUE;
  195. return FALSE;
  196. }
  197. //
  198. // CheckWhichOption()
  199. DWORD
  200. CheckWhichOption(
  201. LPWSTR pszOption )
  202. {
  203. DWORD dw;
  204. WCHAR szOptionTag[ 64 ];
  205. if ( StrCmpNI( pszOption, L"xyzzy", 6 ) == 0 )
  206. return OPTION_VERSIONINGOVERRIDE;
  207. if ( StrCmpNI( pszOption, L"debug", 5 ) == 0 )
  208. return OPTION_DEBUG;
  209. if ( StrCmpNI( pszOption, L"func", 4 ) == 0 )
  210. return OPTION_FUNC;
  211. if ( StrCmpNI( pszOption, L"add", 3 ) == 0 )
  212. return OPTION_ADD;
  213. if ( StrCmpNI( pszOption, L"check", 5 ) == 0 )
  214. return OPTION_CHECK;
  215. if ( StrCmpNI( pszOption, L"upgrade", 7 ) == 0 )
  216. return OPTION_UPGRADE;
  217. if ( StrCmpNI( pszOption, L"auto", 4 ) == 0 )
  218. return OPTION_AUTOMATED;
  219. // Internationalized words
  220. dw = LoadString( g_hinstance, IDS_ADD, szOptionTag, ARRAYSIZE(szOptionTag) );
  221. Assert( dw );
  222. if ( StrCmpNIW( pszOption, szOptionTag, lstrlen(szOptionTag) == 0 ) )
  223. return OPTION_ADD;
  224. dw = LoadString( g_hinstance, IDS_CHECK, szOptionTag, ARRAYSIZE(szOptionTag) );
  225. Assert( dw );
  226. if ( StrCmpNI( pszOption, szOptionTag, lstrlen(szOptionTag) == 0 ) )
  227. return OPTION_CHECK;
  228. return OPTION_UNKNOWN;
  229. }
  230. //
  231. // ParseCommandLine()
  232. // Returns false if the call required useage to be printed else true
  233. //
  234. BOOL
  235. ParseCommandLine( LPWSTR lpCmdLine )
  236. {
  237. LPWSTR psz = lpCmdLine;
  238. while (*psz)
  239. {
  240. if ( *psz == L'/' || *psz == L'-' )
  241. {
  242. LPWSTR pszStartOption = ++psz;
  243. while (*psz && !IsWhiteSpace( *psz ) )
  244. psz++;
  245. *psz = L'\0'; // terminate
  246. switch ( CheckWhichOption( pszStartOption ) )
  247. {
  248. case OPTION_VERSIONINGOVERRIDE:
  249. g_Options.fServerCompatible = TRUE;
  250. break;
  251. #ifdef DEBUG
  252. case OPTION_DEBUG:
  253. g_dwTraceFlags |= TF_HRESULTS;
  254. break;
  255. case OPTION_FUNC:
  256. g_dwTraceFlags |= TF_FUNC;
  257. break;
  258. #endif
  259. case OPTION_ADD:
  260. g_Options.fAddOption = TRUE;
  261. break;
  262. case OPTION_AUTOMATED:
  263. {
  264. LPWSTR pszScriptFilename;
  265. UINT ErrLine;
  266. g_Options.fAutomated = TRUE;
  267. WCHAR UnattendedFile[MAX_PATH];
  268. LPWSTR p;
  269. //
  270. // get the script name
  271. //
  272. //
  273. // first eat all the whitespace
  274. //
  275. psz++;
  276. while(*psz && IsWhiteSpace( *psz )) {
  277. psz++;
  278. }
  279. //
  280. // now get the filename, which may or may not be quoted
  281. //
  282. if (*psz == L'\"') {
  283. pszScriptFilename = ++psz;
  284. while (*psz && ( L'\"' != *psz ) ) {
  285. psz++;
  286. }
  287. } else {
  288. pszScriptFilename = psz;
  289. while (*psz && !IsWhiteSpace( *psz ) ) {
  290. psz++;
  291. }
  292. }
  293. //
  294. // NULL terminate the filename and try to open the file as
  295. // an INF file
  296. //
  297. *psz = L'\0';
  298. g_Options.hinfAutomated = INVALID_HANDLE_VALUE;
  299. if( GetFullPathName( pszScriptFilename,
  300. MAX_PATH,
  301. UnattendedFile,
  302. &p ) ) {
  303. g_Options.hinfAutomated = SetupOpenInfFileW( UnattendedFile, NULL, INF_STYLE_WIN4, &ErrLine );
  304. }
  305. if ( g_Options.hinfAutomated == INVALID_HANDLE_VALUE ) {
  306. ErrorBox( NULL, NULL );
  307. g_Options.fError = TRUE;
  308. }
  309. }
  310. break;
  311. case OPTION_CHECK:
  312. g_Options.fCheckServer = TRUE;
  313. break;
  314. case OPTION_UPGRADE:
  315. g_Options.fUpgrade = TRUE;
  316. break;
  317. case OPTION_UNKNOWN :
  318. default :
  319. WCHAR szCaption[ SMALL_BUFFER_SIZE ];
  320. WCHAR szUsage[ SMALL_BUFFER_SIZE * 2 ];
  321. DWORD dw;
  322. dw = LoadStringW( g_hinstance, IDS_APPNAME, szCaption, ARRAYSIZE( szCaption ) );
  323. Assert( dw );
  324. dw = LoadStringW( g_hinstance, IDS_USAGE, szUsage, ARRAYSIZE( szUsage ));
  325. Assert( dw );
  326. MessageBoxW( NULL, szUsage, szCaption, MB_OK );
  327. return FALSE;
  328. }
  329. }
  330. psz++;
  331. }
  332. return TRUE;
  333. }
  334. //
  335. // DoSetup( )
  336. //
  337. HRESULT
  338. DoSetup( )
  339. {
  340. HRESULT hr = S_OK;
  341. INT iReturn;
  342. //
  343. // Setup dialog
  344. //
  345. iReturn = (INT)DialogBox( g_hinstance,
  346. MAKEINTRESOURCE(IDD_TASKS),
  347. NULL,
  348. SetupDlgProc );
  349. return hr;
  350. }
  351. //
  352. // CheckForReboot( )
  353. //
  354. void
  355. CheckForReboot( )
  356. {
  357. if ( !g_Options.fSISServiceInstalled )
  358. {
  359. MessageBoxFromStrings( NULL,
  360. IDS_MUST_REBOOT_TITLE,
  361. IDS_MUST_REBOOT_MESSAGE,
  362. MB_OK | MB_ICONEXCLAMATION );
  363. SetupPromptReboot( NULL, NULL, FALSE );
  364. }
  365. }
  366. //
  367. // RunningOnNTServer( )
  368. //
  369. BOOL
  370. RunningOnNTServer(void)
  371. {
  372. TraceFunc( "RunningOnNtServer()\n" );
  373. HKEY hkey;
  374. LONG lResult;
  375. WCHAR szProductType[50] = { 0 };
  376. DWORD dwType;
  377. DWORD dwSize = ARRAYSIZE(szProductType);
  378. BOOL fReturn = FALSE; // assume that we are not on NTServer.
  379. // Query the registry for the product type.
  380. lResult = RegOpenKeyEx ( HKEY_LOCAL_MACHINE,
  381. L"System\\CurrentControlSet\\Control\\ProductOptions",
  382. 0,
  383. KEY_READ,
  384. &hkey);
  385. Assert( lResult == ERROR_SUCCESS );
  386. if ( lResult != ERROR_SUCCESS )
  387. goto Error;
  388. lResult = RegQueryValueEx ( hkey,
  389. L"ProductType",
  390. NULL,
  391. &dwType,
  392. (LPBYTE) szProductType,
  393. &dwSize);
  394. Assert( lResult == ERROR_SUCCESS );
  395. RegCloseKey (hkey);
  396. if (lResult != ERROR_SUCCESS)
  397. goto Error;
  398. if ( StrCmpI( szProductType, L"ServerNT" ) == 0 )
  399. {
  400. fReturn = TRUE; // yep. NT Server alright.
  401. }
  402. if ( StrCmpI( szProductType, L"LanmanNT" ) == 0 )
  403. {
  404. fReturn = TRUE; // yep. NT Server alright.
  405. }
  406. Error:
  407. RETURN(fReturn);
  408. }
  409. //
  410. // WinMain()
  411. //
  412. int APIENTRY
  413. WinMain(
  414. HINSTANCE hInstance,
  415. HINSTANCE hPrevInstance,
  416. LPSTR lpCmdLine,
  417. int nCmdShow)
  418. {
  419. TraceFunc( "WinMain( ... )\n" );
  420. HANDLE hMutex;
  421. HRESULT hr = E_FAIL;
  422. WSADATA wsdata;
  423. LPWSTR pszCommandLine = GetCommandLine( );
  424. int iReturn;
  425. g_hinstance = hInstance;
  426. INITIALIZE_TRACE_MEMORY_PROCESS;
  427. // allow only one instance running at a time
  428. hMutex = CreateMutex( NULL, TRUE, L"RemoteBootSetup.Mutext");
  429. if (GetLastError() == ERROR_ALREADY_EXISTS)
  430. {
  431. MessageBoxFromStrings( NULL,
  432. IDS_ALREADY_RUNNING_TITLE,
  433. IDS_ALREADY_RUNNING_MESSAGE,
  434. MB_OK | MB_ICONSTOP );
  435. goto Cleanup;
  436. }
  437. CoInitialize(NULL);
  438. WSAStartup( 0x02, &wsdata );
  439. if( !pSetupIsUserAdmin()
  440. || !pSetupDoesUserHavePrivilege(SE_SHUTDOWN_NAME)
  441. || !pSetupDoesUserHavePrivilege(SE_BACKUP_NAME)
  442. || !pSetupDoesUserHavePrivilege(SE_RESTORE_NAME)
  443. || !pSetupDoesUserHavePrivilege(SE_SYSTEM_ENVIRONMENT_NAME)) {
  444. MessageBoxFromStrings( NULL, IDS_MUST_BE_ADMINISTRATOR_CAPTION, IDS_MUST_BE_ADMINISTRATOR_TEXT, MB_OK );
  445. goto Cleanup;
  446. }
  447. if ( !RunningOnNTServer( ) )
  448. {
  449. MessageBoxFromStrings( NULL, IDS_NOT_RUNNING_ON_NT_SERVER_CAPTION, IDS_NOT_RUNNING_ON_NT_SERVER_TEXT, MB_OK );
  450. goto Cleanup;
  451. }
  452. hr = InitializeOptions( );
  453. if ( FAILED(hr) )
  454. goto Cleanup;
  455. if( !ParseCommandLine( pszCommandLine )) {
  456. goto Cleanup;
  457. }
  458. // Change SetupAPI to Non-backup mode.
  459. // also set a flag that makes it fail all signature checks.
  460. // since we're subject to non-driver signing policy and that
  461. // is set to ignore by default, this means that every copy
  462. // operation will generate a signature warning in setupapi.log
  463. // ...but the memory footprint and speed of risetup process
  464. // will both go down significantly since we won't drag the
  465. // crypto libraries into the process.
  466. //
  467. pSetupSetGlobalFlags( pSetupGetGlobalFlags( ) | PSPGF_NO_BACKUP | PSPGF_AUTOFAIL_VERIFIES );
  468. //
  469. // Go figure out a default for what processor we're
  470. // building an image for.
  471. //
  472. GetProcessorType();
  473. if ( !g_Options.fUpgrade && !g_Options.fAutomated )
  474. {
  475. hr = WizardPages( );
  476. if ( hr != S_OK ) {
  477. goto Cleanup;
  478. }
  479. hr = DoSetup( );
  480. if ( hr != S_OK ) {
  481. goto Cleanup;
  482. }
  483. CheckForReboot( );
  484. }
  485. else if ( g_Options.fAutomated )
  486. {
  487. if ( g_Options.fError ) {
  488. goto Cleanup;
  489. }
  490. hr = GetAutomatedOptions( );
  491. if ( hr != S_OK ) {
  492. goto Cleanup;
  493. }
  494. hr = FindImageSource( NULL );
  495. if ( hr != S_OK ) {
  496. MessageBoxFromStrings( NULL, IDS_FILE_NOT_FOUND_TITLE, IDS_FILE_NOT_FOUND_TEXT, MB_OK );
  497. goto Cleanup;
  498. }
  499. hr = CheckImageSource( NULL );
  500. if ( hr != S_OK ) {
  501. goto Cleanup;
  502. }
  503. hr = CheckInstallation( );
  504. if ( FAILED(hr) ) {
  505. goto Cleanup;
  506. }
  507. hr = DoSetup( );
  508. if ( hr != S_OK ) {
  509. goto Cleanup;
  510. }
  511. }
  512. else if ( g_Options.fUpgrade )
  513. {
  514. hr = UpdateRemoteInstallTree( );
  515. }
  516. else
  517. {
  518. AssertMsg( 0, "How did I get here?" );
  519. }
  520. Cleanup:
  521. CoUninitialize();
  522. if ( hMutex ) {
  523. CloseHandle( hMutex );
  524. }
  525. if ( g_Options.hinf != INVALID_HANDLE_VALUE ) {
  526. SetupCloseInfFile( g_Options.hinf );
  527. }
  528. UNINITIALIZE_TRACE_MEMORY;
  529. RETURN(hr);
  530. }
  531. // stolen from the CRT, used to shrink our code
  532. int _stdcall ModuleEntry(void)
  533. {
  534. int i;
  535. STARTUPINFOA si;
  536. LPSTR pszCmdLine = GetCommandLineA();
  537. if ( *pszCmdLine == '\"' )
  538. {
  539. /*
  540. * Scan, and skip over, subsequent characters until
  541. * another double-quote or a null is encountered.
  542. */
  543. while ( *++pszCmdLine && (*pszCmdLine != '\"') );
  544. /*
  545. * If we stopped on a double-quote (usual case), skip
  546. * over it.
  547. */
  548. if ( *pszCmdLine == '\"' )
  549. pszCmdLine++;
  550. }
  551. else
  552. {
  553. while (*pszCmdLine > ' ')
  554. pszCmdLine++;
  555. }
  556. /*
  557. * Skip past any white space preceeding the second token.
  558. */
  559. while (*pszCmdLine && (*pszCmdLine <= ' '))
  560. {
  561. pszCmdLine++;
  562. }
  563. si.dwFlags = 0;
  564. GetStartupInfoA(&si);
  565. i = WinMain(GetModuleHandle(NULL), NULL, pszCmdLine,
  566. si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT);
  567. ExitProcess(i);
  568. return i; // We never come here.
  569. }