/**************************************************************************** Copyright (c) Microsoft Corporation 1997 All rights reserved ***************************************************************************/ #include "pch.h" #include #include #include #include #include #include "utils.h" DEFINE_MODULE("Main"); // Globals HINSTANCE g_hinstance = NULL; #define SMALL_BUFFER_SIZE 256 #define MAX_FILES_SIZE 512 #define STRING_BUFFER_SIZE 65535 static TCHAR g_szServerName[ SMALL_BUFFER_SIZE ] = { 0 }; static TCHAR g_szRemoteBoot[ SMALL_BUFFER_SIZE ] = { 0 }; static TCHAR g_szBootFilename[ SMALL_BUFFER_SIZE ] = { 0 }; static TCHAR g_szBootIniOptions[ SMALL_BUFFER_SIZE ] = { 0 }; static TCHAR g_szClientName[ SMALL_BUFFER_SIZE ] = { 0 }; static TCHAR g_szMAC[ SMALL_BUFFER_SIZE ] = { 0 }; static TCHAR g_szInstallation[ SMALL_BUFFER_SIZE ] = { 0 }; static TCHAR g_szClientDomain[ SMALL_BUFFER_SIZE ] = { 0 }; static TCHAR g_szAdminUser[ SMALL_BUFFER_SIZE ] = { 0 }; static TCHAR g_szAdminPasswd[ SMALL_BUFFER_SIZE ] = { 0 }; static TCHAR g_szWinntTemplate[ SMALL_BUFFER_SIZE ] = { 0 }; // search and replace structure typedef struct { LPTSTR pszToken; LPTSTR pszString; } SAR, * LPSAR; // // Searches and replaces text. // // NOTE: There is no check for writing beyond the buffer even though // I passed the paramater. // void SearchAndReplace( LPSAR psarList, LPTSTR pszString, DWORD dwSize ) { LPTSTR psz = pszString; if ( !psarList || !pszString ) return; while ( *psz ) { if ( *psz == TEXT('%') ) { LPSAR psar = psarList; psz++; // move forward while( psar->pszToken ) { int iCmp; DWORD dwString = lstrlen( psar->pszString ); DWORD dwToken = lstrlen( psar->pszToken ); LPTSTR pszTemp = psz + dwToken; TCHAR ch = *pszTemp; *pszTemp = 0; iCmp = lstrcmpi( psz, psar->pszToken ); *pszTemp = ch; if ( !iCmp ) { // match, so replace psz--; // move back if ( 2 + dwToken < dwString ) { DWORD dwLen = lstrlen( &psz[ 2 + dwToken ] ) + 1; MoveMemory( &psz[ dwString ], &psz[ 2 + dwToken ], dwLen * sizeof(TCHAR)); } CopyMemory( psz, psar->pszString, dwString * sizeof(TCHAR) ); if ( 2 + dwToken > dwString ) { lstrcpy( &psz[ dwString ], &psz[ 2 + dwToken ] ); } psz++; // move forward break; } psar++; } } else { psz++; } } } // // Munge the registry // LONG MungeRegistry( LPCTSTR pszPath, LPCTSTR pszKey, LPTSTR pszResult, LPDWORD pdwSize ) { HKEY hkeyComputer; LONG lResult; lResult = RegOpenKey( HKEY_LOCAL_MACHINE, pszPath, &hkeyComputer ); if ( lResult != ERROR_SUCCESS ) goto Finish; lResult = RegQueryValueEx( hkeyComputer, pszKey, NULL, // reserved NULL, // type (LPBYTE) pszResult, pdwSize ); RegCloseKey( hkeyComputer ); Finish: return lResult; } // // Munges the registry for the computer name // LONG RetrieveComputerName( void ) { DWORD dwSize = sizeof( g_szServerName ); return MungeRegistry( REGSTR_PATH_COMPUTRNAME, REGSTR_VAL_COMPUTERNAME, g_szServerName, &dwSize ); } // // Populates the Installation ComboBox // HRESULT PopulateInstallationComboBox( HWND hDlg ) { BOOL fKeepSearching = TRUE; HRESULT hr = S_OK; TCHAR szPath[ MAX_PATH ]; DWORD dwLen; WIN32_FIND_DATA fd; HANDLE handle; HWND hwndCB = GetDlgItem( hDlg, IDC_CB_INSTALLATION ); LPSHARE_INFO_2 psi = NULL; NetShareGetInfo( NULL, // this machine g_szRemoteBoot, 2, // share level 2 (LPBYTE *) &psi ); // create the directory lstrcpy( szPath, psi->shi2_path ); dwLen = lstrlen( szPath ); szPath[ dwLen++ ] = TEXT('\\'); LoadString( g_hinstance, IDS_SETUP, &szPath[ dwLen ], ARRAYSIZE( szPath ) - dwLen ); dwLen = lstrlen( szPath ); lstrcpy( &szPath[ dwLen ], TEXT("\\*") ); handle = FindFirstFile( szPath, &fd ); if ( handle == INVALID_HANDLE_VALUE ) goto Cleanup; while ( fKeepSearching) { if ( fd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY && lstrcmp( fd.cFileName, TEXT(".") ) && // ignore lstrcmp( fd.cFileName, TEXT("..") ) ) // ignore { ComboBox_AddString( hwndCB, fd.cFileName ); } fKeepSearching = FindNextFile( handle, &fd ); } ComboBox_SetCurSel( hwndCB, 0 ); Cleanup: if ( handle != INVALID_HANDLE_VALUE) FindClose( handle ); if ( psi ) NetApiBufferFree( psi ); return hr; } // // Populates the Configuration ComboBox // HRESULT PopulateConfigurationComboBox( HWND hDlg ) { BOOL fKeepSearching = TRUE; HRESULT hr = S_OK; TCHAR szPath[ MAX_PATH ]; DWORD dwLen; WIN32_FIND_DATA fd; HANDLE handle; HWND hwndCB = GetDlgItem( hDlg, IDC_CB_WINNTSIF ); LPSHARE_INFO_2 psi = NULL; int iSel; ComboBox_ResetContent( hwndCB ); NetShareGetInfo( NULL, // this machine g_szRemoteBoot, 2, // share level 2 (LPBYTE *) &psi ); // create the directory lstrcpy( szPath, psi->shi2_path ); dwLen = lstrlen( szPath ); szPath[ dwLen++ ] = TEXT('\\'); LoadString( g_hinstance, IDS_TEMPLATES, &szPath[ dwLen ], ARRAYSIZE( szPath ) - dwLen ); dwLen = lstrlen( szPath ); szPath[ dwLen++ ] = TEXT('\\'); LoadString( g_hinstance, IDS_INTELPATH, &szPath[ dwLen ], ARRAYSIZE( szPath ) - dwLen ); dwLen = lstrlen( szPath ); szPath[ dwLen++ ] = TEXT('\\'); LoadString( g_hinstance, IDS_WINNTTEMPLATEFILES, &szPath[ dwLen ], ARRAYSIZE( szPath ) - dwLen ); handle = FindFirstFile( szPath, &fd ); if ( handle == INVALID_HANDLE_VALUE ) goto Cleanup; while ( fKeepSearching) { // whack it at the period LPTSTR psz = fd.cFileName; while ( *psz ) { if ( *psz == TEXT('.') ) { *psz = 0; break; } psz++; } ComboBox_AddString( hwndCB, fd.cFileName ); fKeepSearching = FindNextFile( handle, &fd ); } ComboBox_SetCurSel( hwndCB, 0 ); Cleanup: if ( handle != INVALID_HANDLE_VALUE) FindClose( handle ); if ( psi ) NetApiBufferFree( psi ); return hr; } // // // BOOL CALLBACK ClientDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) { NMHDR FAR *lpnmhdr; DWORD dw; switch ( uMsg ) { case WM_INITDIALOG: CenterDialog( hDlg ); SetDlgItemText( hDlg, IDC_E_SERVER, g_szServerName ); SetDlgItemText( hDlg, IDC_E_REMOTEBOOT, g_szRemoteBoot ); SetDlgItemText( hDlg, IDC_E_BOOTFILENAME, g_szBootFilename ); SetDlgItemText( hDlg, IDC_E_BOOTINIOPTIONS, g_szBootIniOptions); PopulateInstallationComboBox( hDlg ); PopulateConfigurationComboBox( hDlg ); Edit_LimitText( GetDlgItem( hDlg, IDC_E_MAC ), 12 ); break; case WM_COMMAND: { switch ( LOWORD( wParam ) ) { case IDOK: { DWORD dwLen; TCHAR sz[ SMALL_BUFFER_SIZE ]; GetDlgItemText( hDlg, IDC_E_SERVER, g_szServerName, ARRAYSIZE( g_szServerName )); GetDlgItemText( hDlg, IDC_E_REMOTEBOOT, g_szRemoteBoot, ARRAYSIZE( g_szRemoteBoot )); GetDlgItemText( hDlg, IDC_E_BOOTFILENAME, g_szBootFilename, ARRAYSIZE( g_szBootFilename )); GetDlgItemText( hDlg, IDC_E_BOOTINIOPTIONS, g_szBootIniOptions, ARRAYSIZE( g_szBootIniOptions )); GetDlgItemText( hDlg, IDC_E_MAC, g_szMAC, ARRAYSIZE( g_szMAC )); GetDlgItemText( hDlg, IDC_E_MACHINENAME, g_szClientName, ARRAYSIZE( g_szClientName )); GetDlgItemText( hDlg, IDC_CB_INSTALLATION, g_szInstallation, ARRAYSIZE( g_szInstallation )); GetDlgItemText( hDlg, IDC_E_CLIENTDOMAIN, g_szClientDomain, ARRAYSIZE( g_szClientDomain )); GetDlgItemText( hDlg, IDC_E_ADMINUSER, g_szAdminUser, ARRAYSIZE( g_szAdminUser )); GetDlgItemText( hDlg, IDC_E_ADMINPASSWD, g_szAdminPasswd, ARRAYSIZE( g_szAdminPasswd )); GetDlgItemText( hDlg, IDC_CB_WINNTSIF, g_szWinntTemplate, ARRAYSIZE( g_szWinntTemplate )); // add that extension dw = LoadString( g_hinstance, IDS_WINNTTEMPLATEFILES, sz, ARRAYSIZE( sz )); Assert( dw ); dwLen = lstrlen( g_szWinntTemplate ); lstrcpy( &g_szWinntTemplate[ dwLen ], &sz[ 1 ] ); EndDialog( hDlg, IDOK ); } break; case IDCANCEL: EndDialog( hDlg, IDCANCEL ); break; } } break; default: return FALSE; } return TRUE; } // // change semicolon delinated list to double-null list // void SemiColonToDoubleNullList( LPTSTR pszList ) { while ( *pszList ) { if ( *pszList == TEXT(';') ) { *pszList = 0; } pszList++; } pszList++; *pszList = 0; // double the null. } // // Adds files to the Queue to be copied. It returns the number of files added // to the Queue. // DWORD CopyFilesAddToQueue( HSPFILEQ Queue, // setup Queue LPTSTR pszSource, LPTSTR pszDest, LPTSTR pszFiles, // Double-null terminated file list LPTSTR pszSubpath ) // optional sub-path { DWORD dwCount = 0; LPTSTR psz = pszFiles; while ( *pszFiles ) { DWORD dwLen; // check for comma which indicates rename psz = pszFiles; while (*psz && *psz != TEXT(',')) psz++; if ( *psz == TEXT(',') ) { *psz= 0; // terminate psz++; } else { // sources name is dest name psz = pszFiles; } SetupQueueCopy( Queue, pszSource, NULL, pszFiles, NULL, NULL, pszDest, psz, SP_COPY_NEWER | SP_COPY_NOOVERWRITE | SP_COPY_WARNIFSKIP ); // get next file pszFiles = psz + lstrlen( psz ) + 1; dwCount++; } return dwCount; } // // // HRESULT SetupClient( ) { HRESULT hr = E_FAIL; TCHAR szImage[ MAX_PATH ]; TCHAR szSetup[ MAX_PATH ]; TCHAR szTemplates[ MAX_PATH ]; TCHAR szBootIni[ MAX_PATH ]; TCHAR szString[ MAX_PATH ]; TCHAR szDosNetFilename[ MAX_PATH ]; TCHAR szWinntSif[ MAX_PATH ]; DWORD dwLen; DWORD dw; HSPFILEQ Queue; PVOID pContext; HANDLE hFile = INVALID_HANDLE_VALUE; HKEY hkeyBINL; HKEY hkeyMAC; LPTSTR pszFiles = (LPTSTR) TraceAlloc( GMEM_FIXED, MAX_FILES_SIZE ); LPTSTR psz = NULL; LPSHARE_INFO_2 psi = NULL; LPVOID args[ 6 ]; SAR sExpand[] = { { TEXT("BINLSERVER"), g_szServerName }, { TEXT("INSTALLATION"), g_szInstallation }, { TEXT("CLIENTNAME"), g_szClientName }, { TEXT("REMOTEBOOT"), g_szRemoteBoot }, { TEXT("CLIENTDOMAIN"), g_szClientDomain }, { TEXT("ADMINUSER"), g_szAdminUser }, { TEXT("ADMINPASSWD"), g_szAdminPasswd }, { NULL, NULL } // end of list }; char chString[ STRING_BUFFER_SIZE ]; NetShareGetInfo( NULL, // this machine g_szRemoteBoot, 2, // share level 2 (LPBYTE *) &psi ); // create the directory lstrcpy( szImage, psi->shi2_path ); dwLen = lstrlen( szImage ); szImage[ dwLen++ ] = TEXT('\\'); dw = LoadString( g_hinstance, IDS_IMAGES, &szImage[ dwLen ], ARRAYSIZE( szImage ) - dwLen ); Assert( dw ); dwLen = lstrlen( szImage ); szImage[ dwLen++ ] = TEXT('\\'); lstrcpy( &szImage[ dwLen ], g_szClientName ); CreateDirectory( szImage, NULL ); // setup path lstrcpy( szSetup, psi->shi2_path ); dwLen = lstrlen( szSetup ); szSetup[ dwLen++ ] = TEXT('\\'); dw = LoadString( g_hinstance, IDS_SETUP, &szSetup[ dwLen ], ARRAYSIZE( szSetup ) - dwLen ); Assert( dw ); dwLen = lstrlen( szSetup ); szSetup[ dwLen++ ] = TEXT('\\'); lstrcpy( &szSetup[ dwLen ], g_szInstallation ); dwLen = lstrlen( szSetup ); szSetup[ dwLen++ ] = TEXT('\\'); dw = LoadString( g_hinstance, IDS_INTELPATH, &szSetup[ dwLen ], ARRAYSIZE( szSetup ) - dwLen ); Assert( dw ); // Create DOSNET.INF filepath lstrcpy( szDosNetFilename, szSetup ); dwLen = lstrlen( szDosNetFilename ); szDosNetFilename[ dwLen ] = TEXT('\\'); dwLen++; dw = LoadString( g_hinstance, IDS_DOSNETINFFILENAME, &szDosNetFilename[ dwLen ], ARRAYSIZE( szDosNetFilename ) - dwLen ); Assert( dw ); Queue = SetupOpenFileQueue( ); // Retrieve the list of files from the INF and add to Queue GetPrivateProfileSection( TEXT("RootBootFiles"), pszFiles, MAX_FILES_SIZE, szDosNetFilename ); CopyFilesAddToQueue( Queue, szSetup, szImage, pszFiles, NULL ); // add additional files from resources dw = LoadString( g_hinstance, IDS_FILESTOBECOPIED, pszFiles, MAX_FILES_SIZE ); Assert( dw ); SemiColonToDoubleNullList( pszFiles ); CopyFilesAddToQueue( Queue, szSetup, szImage, pszFiles, NULL ); // copy winnt.sif template lstrcpy( szTemplates, psi->shi2_path ); dwLen = lstrlen( szTemplates ); szTemplates[ dwLen++ ] = TEXT('\\'); dw = LoadString( g_hinstance, IDS_TEMPLATES, &szTemplates[ dwLen ], ARRAYSIZE( szTemplates ) - dwLen ); Assert( dw ); dwLen = lstrlen( szTemplates ); szTemplates[ dwLen++ ] = TEXT('\\'); dw = LoadString( g_hinstance, IDS_INTELPATH, &szTemplates[ dwLen ], ARRAYSIZE( szTemplates ) - dwLen ); Assert( dw ); lstrcpy( pszFiles, g_szWinntTemplate ); dwLen = lstrlen( pszFiles ); pszFiles[ dwLen++ ] = TEXT(','); dw = LoadString( g_hinstance, IDS_WINNTSIF, &pszFiles[ dwLen ], MAX_PATH ); Assert( dw ); SemiColonToDoubleNullList( pszFiles ); CopyFilesAddToQueue( Queue, szTemplates, szImage, pszFiles, NULL ); TraceFree( pszFiles ); pContext = SetupInitDefaultQueueCallback( NULL ); if (!SetupCommitFileQueue( NULL, Queue, SetupDefaultQueueCallback, pContext ) ) goto Cleanup; dw = LoadString( g_hinstance, IDS_REG_BINL_PARAMETER, szString, ARRAYSIZE( szString )); Assert( dw ); if ( ERROR_SUCCESS == RegOpenKey( HKEY_LOCAL_MACHINE, szString, &hkeyBINL ) ) { if ( ERROR_SUCCESS == RegCreateKey( hkeyBINL, g_szMAC, &hkeyMAC ) ) { dw = LoadString( g_hinstance, IDS_IMAGES, szString, ARRAYSIZE( dw )); Assert( dw ); dwLen = lstrlen( szString ); szString[ dwLen++ ] = TEXT('\\'); lstrcpy( &szString[ dwLen ], g_szClientName ); dwLen = lstrlen( szString ); szString[ dwLen++ ] = TEXT('\\'); lstrcpy( &szString[ dwLen ], g_szBootFilename ); dwLen = ( lstrlen( szString ) + 1 ) * sizeof(TCHAR); RegSetValueEx( hkeyMAC, TEXT("BootFileName"), 0, REG_SZ, (LPBYTE) szString, dwLen ); dwLen = ( lstrlen( g_szServerName ) + 1 ) * sizeof(TCHAR); RegSetValueEx( hkeyMAC, TEXT("HostName"), 0, REG_SZ, (LPBYTE) g_szServerName, dwLen ); RegCloseKey( hkeyMAC ); } RegCloseKey( hkeyBINL ); } // create MAC Address file lstrcpy( szString, szImage ); dwLen = lstrlen( szString ); szString[ dwLen++ ] = TEXT('\\'); lstrcpy( &szString[ dwLen ], g_szMAC ); hFile = CreateFile( szString, GENERIC_WRITE, FILE_SHARE_READ, NULL, // security attribs CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, // maybe FILE_ATTRIBUTE_HIDDEN NULL ); // template CloseHandle( hFile ); lstrcpy( szBootIni, szImage ); dwLen = lstrlen( szBootIni ); szBootIni[ dwLen++ ] = TEXT('\\'); dw = LoadString( g_hinstance, IDS_BOOTINI, &szBootIni[ dwLen ], ARRAYSIZE( szBootIni ) - dwLen ); Assert( dw ); hFile = CreateFile( szBootIni, GENERIC_WRITE, FILE_SHARE_READ, NULL, // security attribs CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, // maybe FILE_ATTRIBUTE_HIDDEN NULL ); // template if ( hFile == INVALID_HANDLE_VALUE ) goto Cleanup; dw = LoadString( g_hinstance, IDS_BOOTLOADER, szString, ARRAYSIZE( szString )); Assert( dw ); args[0] = (LPVOID) &g_szServerName; args[1] = (LPVOID) &g_szRemoteBoot; args[2] = (LPVOID) &g_szClientName; args[3] = (LPVOID) &g_szInstallation; args[4] = (LPVOID) &g_szBootIniOptions; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, szString, NULL, // message id - n/a NULL, // language - use system (LPTSTR) &psz, 0, // minimum length (char **) &args ); DebugMemoryAddAddress( psz ); WideCharToMultiByte( CP_ACP, 0, psz, -1, chString, ARRAYSIZE( chString ), NULL, NULL ); dwLen = lstrlenA( chString ); WriteFile( hFile, chString, dwLen, &dw, NULL ); CloseHandle( hFile ); TraceFree( psz ); // process WINNT.SIF lstrcpy( szWinntSif, szImage ); dwLen = lstrlen( szWinntSif ); szWinntSif[ dwLen++ ] = TEXT('\\'); dw = LoadString( g_hinstance, IDS_WINNTSIF, &szWinntSif[ dwLen ], ARRAYSIZE( szWinntSif ) - dwLen ); Assert( dw ); hFile = CreateFile( szWinntSif, GENERIC_READ, FILE_SHARE_READ, NULL, // security attribs OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, // maybe FILE_ATTRIBUTE_HIDDEN NULL ); // template if ( hFile == INVALID_HANDLE_VALUE ) goto Cleanup; ReadFile( hFile, chString, ARRAYSIZE( chString ), &dw, NULL ); Assert( dw != ARRAYSIZE( chString )); CloseHandle( hFile ); psz = (LPTSTR) TraceAlloc( GMEM_FIXED, ARRAYSIZE( chString )); MultiByteToWideChar( CP_ACP, 0, chString, -1, psz, ARRAYSIZE( chString )); SearchAndReplace( sExpand, psz, ARRAYSIZE( chString )); hFile = CreateFile( szWinntSif, GENERIC_WRITE, FILE_SHARE_READ, NULL, // security attribs CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, // maybe FILE_ATTRIBUTE_HIDDEN NULL ); // template if ( hFile == INVALID_HANDLE_VALUE ) goto Cleanup; WideCharToMultiByte( CP_ACP, 0, psz, -1, chString, ARRAYSIZE( chString ), NULL, NULL ); dwLen = lstrlenA( chString ); WriteFile( hFile, chString, dwLen, &dw, NULL ); CloseHandle( hFile ); TraceFree( psz ); hr = S_OK; Cleanup: if ( Queue ) SetupCloseFileQueue( Queue ); if ( psi ) NetApiBufferFree( psi ); return hr; } // // WinMain() // int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HANDLE hMutex; HRESULT hr = S_OK; DWORD dw; g_hinstance = hInstance; INITIALIZE_TRACE_MEMORY; // Initialize RetrieveComputerName( ); dw = LoadString( g_hinstance, IDS_REMOTEBOOT, g_szRemoteBoot, ARRAYSIZE( g_szRemoteBoot )); Assert( dw ); dw = LoadString( g_hinstance, IDS_BOOTFILENAME, g_szBootFilename, ARRAYSIZE( g_szBootFilename )); Assert( dw ); dw = LoadString( g_hinstance, IDS_BOOTINIOPTIONS, g_szBootIniOptions, ARRAYSIZE( g_szBootIniOptions )); Assert( dw ); if ( IDOK == DialogBox( g_hinstance, MAKEINTRESOURCE( IDD_CLIENT ), NULL, ClientDlgProc ) ) { if ( lstrlen( g_szMAC ) != 12 ) goto Cleanup; if ( !lstrlen( g_szClientName ) ) goto Cleanup; hr = SetupClient( ); } Cleanup: UNINITIALIZE_TRACE_MEMORY; RRETURN(hr); } // stolen from the CRT, used to shrink our code int _stdcall ModuleEntry(void) { int i; STARTUPINFOA si; LPSTR pszCmdLine = GetCommandLineA(); if ( *pszCmdLine == '\"' ) { /* * Scan, and skip over, subsequent characters until * another double-quote or a null is encountered. */ while ( *++pszCmdLine && (*pszCmdLine != '\"') ); /* * If we stopped on a double-quote (usual case), skip * over it. */ if ( *pszCmdLine == '\"' ) pszCmdLine++; } else { while (*pszCmdLine > ' ') pszCmdLine++; } /* * Skip past any white space preceeding the second token. */ while (*pszCmdLine && (*pszCmdLine <= ' ')) { pszCmdLine++; } si.dwFlags = 0; GetStartupInfoA(&si); i = WinMain(GetModuleHandle(NULL), NULL, pszCmdLine, si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT); ExitProcess(i); return i; // We never come here. }