/****************************************************************************

   Copyright (c) Microsoft Corporation 1997
   All rights reserved
 
 ***************************************************************************/

#include "pch.h"

#include <windowsx.h>
#include <setupapi.h>
#include <advpub.h>
#include <regstr.h>
#include <lm.h>

#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.
}