/*++

Copyright (c) 1999 Microsoft Corporation

Module Name:

    smbrdr.c

Abstract:

    This module implements the functios to load and unload the
    smb monolithic minirdr. Also explicit start/stop control is
    provided

    This module also populates the registry entries for the
    driver, and the network provider.

--*/
//#ifndef UNICODE
//#define UNICODE
//#endif

#include <windows.h>
#include <devioctl.h>
#include <stdlib.h>

#include "srfunc.h"


#ifdef DBG
#define DbgP(_x_) DbgPrint _x_
#else
#define DbgP(_x_)
#endif

ULONG _cdecl DbgPrint( LPTSTR Format, ... );

#define TRACE_TAG   L"SMBRDR:    "



TCHAR* SmbMrxDriverName = TEXT("SmbMRx");


// load action states

ULONG_PTR LoadActionStates[] =
{
    RDR_NULL_STATE,
    RDR_LOADING,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE
};

// unload action states
ULONG_PTR UnloadActionStates[] =
{
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_UNLOADING,
    RDR_UNLOADING,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE
};

// Start action states
ULONG_PTR StartActionStates[] =
{
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_STARTING,
    RDR_STARTING,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE
};

// Stop action states
ULONG_PTR StopActionStates[] =
{
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_STOPPING
};

ULONG_PTR TransitionActionStates[] =
{
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_UNLOADED,
    RDR_LOADED,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_STOPPED,
    RDR_STARTED,
    RDR_NULL_STATE
};

ULONG_PTR ErrorActionStates[] =
{
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_LOADED,
    RDR_UNLOADED,
    RDR_NULL_STATE,
    RDR_NULL_STATE,
    RDR_STARTED,
    RDR_STOPPED,
    RDR_NULL_STATE
};

ULONG_PTR NoneActionStates[] =
{
    RDR_NULL_STATE,
    RDR_UNLOADED,
    RDR_UNLOADING,
    RDR_LOADING,
    RDR_LOADED,
    RDR_STOPPED,
    RDR_STOPPING,
    RDR_STARTING,
    RDR_STARTED
};


ULONG_PTR *ActionsStatesArray[] =
{
    LoadActionStates,
    UnloadActionStates,
    StartActionStates,
    StopActionStates,
    TransitionActionStates,
    ErrorActionStates,
    NoneActionStates
};

ACTIONVECTOR ActionProcs[] =
{
    RdrLoad,
    RdrUnload,
    RdrStart,
    RdrStop,
    RdrDoNothing,
    RdrDoNothing,
    RdrDoNothing
};


typedef enum _INSTALLCHECKS
{
    installcheck_start,
    installcheck_driverfile,
    installcheck_providerfile,
    installcheck_serviceentry,
    installcheck_providerorder,
    installcheck_stop,
    installcheck_done
};


ULONG_PTR RdrInstallCheck( void )
{
    TCHAR tszTestPath[_MAX_PATH];
    ULONG_PTR teststep, len;
    ULONG_PTR tc = SETUP_COMPLETE;
    HANDLE  th;

    for ( teststep = installcheck_start; teststep < installcheck_done; teststep++ )
    {
        switch ( teststep )
        {
            case installcheck_start:
            {
                len = GetWindowsDirectory( tszTestPath, _MAX_PATH );
            }
            break;

            case installcheck_driverfile:
            {
                lstrcpy( &tszTestPath[len], DRIVER_FILE_PATH );
                th = CreateFile( tszTestPath, 0, 0, NULL, OPEN_EXISTING, 0, NULL );
                if ( th == INVALID_HANDLE_VALUE )
                {
                    tc = SETUP_MISSING_FILE;
                    teststep = installcheck_stop;
                }
                else
                {
                    CloseHandle( th );
                }
            }
            break;

            case installcheck_providerfile:
            {
                lstrcpy( &tszTestPath[len], PROVIDER_FILE_PATH );
                th = CreateFile( tszTestPath, 0, 0, NULL, OPEN_EXISTING, 0, NULL );
                if ( th == INVALID_HANDLE_VALUE )
                {
                    tc = SETUP_MISSING_FILE;
                    teststep = installcheck_stop;
                }
                else
                {
                    CloseHandle( th );
                }
            }
            break;

            case installcheck_serviceentry:
            {
                HKEY hTestKey;

                if ( OpenKey( RDRSERVICE_KEY, &hTestKey ) )
                {
                    RegCloseKey( hTestKey );
                }
                else
                {
                    tc = SETUP_INCOMPLETE;
                    teststep = installcheck_stop;
                }
            }
            break;

            case installcheck_providerorder:
            {
                LPTSTR pOrder;

                RdrGetProviderOrderString( &pOrder );
                if ( pOrder )
                {
                    if ( !RdrFindProviderInOrder( pOrder, PROVIDER_NAME ) )
                    {
                        tc = SETUP_INCOMPLETE;
                        teststep = installcheck_stop;
                    }
                    free( pOrder );
                }
            }
            break;

            case installcheck_stop:
                break;
        }
    }

    return tc;
}

BOOL RdrCompleteSetup( void )
{
    return RdrSetupServiceEntry( ) && RdrSetupProviderOrder( );
}

// These handles are retained

HANDLE hSharedMemory;
HANDLE hMutex;


BOOL RdrStart(void)
/*++

Routine Description:

    This routine starts the SMB sample mini redirector.

Notes:

    The start is distinguished from Load. During this phase the appropriate FSCTL
    is issued and the shared memory/mutex data structures required for the Network
    provider DLL are initialized.

--*/
{

    HANDLE  DeviceHandle;       // The mini rdr device handle
    DWORD   BytesRet;
    BOOL    started = FALSE;

    // Grab a handle to the redirector device object

    DbgP((TEXT("Opening Rdr Device Object for Start Ioctl\n")));
    DeviceHandle = CreateFile( DD_SMBMRX_USERMODE_DEV_NAME,
                               GENERIC_READ | GENERIC_WRITE,
                               FILE_SHARE_READ | FILE_SHARE_WRITE,
                               NULL,
                               OPEN_EXISTING,
                               0,
                               NULL );

    DbgP((TEXT("returned from rdr device open\n")));

    if ( DeviceHandle != INVALID_HANDLE_VALUE )
    {
        DbgP(( TEXT("Issueing Rdr Start Ioctl\n") ));
        started = DeviceIoControl( DeviceHandle,
                                   IOCTL_SMBMRX_START,
                                   NULL,
                                   0,
                                   NULL,
                                   0,
                                   &BytesRet,
                                   NULL );

        // Create a section of shared memory to serve as the connection database
        if ( started )
        {
            DWORD  Status;
            HANDLE hMutex;

            hSharedMemory = CreateFileMapping( INVALID_HANDLE_VALUE,
                                               NULL,
                                               PAGE_READWRITE,
                                               0,
                                               sizeof(SMBMRXNP_SHARED_MEMORY),
                                               SMBMRXNP_SHARED_MEMORY_NAME);

            if (hSharedMemory == NULL)
            {
                Status = GetLastError();

                DbgP((TEXT("SMB MRx Net Provider shared memory Creation status %lx\n"),Status));
            }
            else
            {
                PSMBMRXNP_SHARED_MEMORY pSharedMemory;

                pSharedMemory = MapViewOfFile(hSharedMemory, FILE_MAP_WRITE, 0, 0, 0);

                if (pSharedMemory != NULL)
                {
                      pSharedMemory->HighestIndexInUse      = -1;
                      pSharedMemory->NumberOfResourcesInUse =  0;
                }

                UnmapViewOfFile(pSharedMemory);
            }

            hMutex = CreateMutex( NULL,
                                  FALSE,
                                  SMBMRXNP_MUTEX_NAME);

            if (hMutex == NULL)
            {
                Status = GetLastError();
                DbgP(( TEXT("SMB MRx Net Provider Mutex Creation status %lx\n"), Status));
            }
        }
        else
        {
            DbgP(( TEXT("The DeviceIoctl for Starting Redirector returned %lx\n"), GetLastError() ));
        }

    }
    else
    {
        DbgP(( TEXT("The CreateFile for opening device failed with error 0x%lx\n"), GetLastError() ));
        DbgP(( TEXT("Device is %s\n"),DD_SMBMRX_USERMODE_DEV_NAME ));
    }

    //DbgP((TEXT("SMB MRx sample mini redirector start status %lx\n"),ntstatus));

    CloseHandle(DeviceHandle);

    return started;


}

BOOL RdrStop( void )
/*++

Routine Description:

    This routine stops the SMB sample mini redirector.

Notes:

    The stop is distinguished from unload. During this phase the appropriate FSCTL
    is issued and the shared memory/mutex data structures required for the Network
    provider DLL are torn down.

--*/
{
    HANDLE  DeviceHandle;       // The mini rdr device handle
    DWORD   BytesRet;
    BOOL    stopped = FALSE;

    // Grab a handle to the redirector device object

    DeviceHandle = CreateFile( DD_SMBMRX_USERMODE_DEV_NAME,
                               GENERIC_READ | GENERIC_WRITE,
                               FILE_SHARE_READ | FILE_SHARE_WRITE,
                               NULL,
                               OPEN_EXISTING,
                               0,
                               NULL );

    DbgP((TEXT("Doing Stop DeviceIoControl\n")));
    if ( DeviceHandle != INVALID_HANDLE_VALUE )
    {
        stopped = DeviceIoControl( DeviceHandle,
                                   IOCTL_SMBMRX_STOP,
                                   NULL,
                                   0,
                                   NULL,
                                   0,
                                   &BytesRet,
                                   NULL );

        CloseHandle( DeviceHandle );
    }
    else
    {
        DbgP(( TEXT("The CreateFile for opening device failed\n") ));
    }

    CloseHandle(hMutex);
    CloseHandle(hSharedMemory);

//    DbgP(( TEXT("SMB MRx sample mini redirector start status %lx\n"),ntstatus ));

    return stopped;
}


BOOL RdrLoad( void )
{
    SC_HANDLE sch, service;
    BOOL loaded = FALSE;

    DbgP((TEXT("Loading SMB sample minirdr.......\n")));

    sch = OpenSCManager( NULL, NULL, GENERIC_EXECUTE );
    if ( sch )
    {
        service = OpenService( sch, RDRSERVICE, GENERIC_EXECUTE );
        if ( service )
        {
            loaded = StartService( service, 0, NULL );
            CloseServiceHandle( service );
        }
        CloseServiceHandle( sch );
    }

    return loaded;
}


BOOL RdrUnload( void )
{
    SC_HANDLE sch, service;
    BOOL unloaded = FALSE;

    DbgP((TEXT("Unloading SMB sample minirdr.......\n")));

    sch = OpenSCManager( NULL, NULL, GENERIC_EXECUTE );
    if ( sch )
    {
        service = OpenService( sch, RDRSERVICE, GENERIC_EXECUTE );
        if ( service )
        {
            SERVICE_STATUS ss;

            unloaded = ControlService( service, SERVICE_CONTROL_STOP, &ss );
            CloseServiceHandle( service );
        }
        CloseServiceHandle( sch );
    }

    return unloaded;
}


BOOL RdrDoNothing( void )
{
    return TRUE;
}


BOOL RdrDoAction( ULONG_PTR action )
{
    return (*ActionProcs[action])( );
}


ULONG_PTR RdrGetInitialState(void)
{
    ULONG_PTR state = RDR_UNLOADED;
    SC_HANDLE sch, service;

    sch = OpenSCManager( NULL, NULL, GENERIC_READ );
    if ( sch )
    {
        service = OpenService( sch, RDRSERVICE, GENERIC_READ );
        if ( service )
        {
            SERVICE_STATUS ss;

            if ( QueryServiceStatus( service, &ss ) )
            {
                switch ( ss.dwCurrentState )
                {
                    case SERVICE_STOPPED:
                        state = RDR_UNLOADED;
                        break;
                    case SERVICE_START_PENDING:
                        state = RDR_LOADING;
                        break;
                    case SERVICE_STOP_PENDING:
                        state = RDR_UNLOADING;
                        break;
                    case SERVICE_RUNNING:
                        state = RDR_LOADED;
                        break;
                    case SERVICE_CONTINUE_PENDING:
                    case SERVICE_PAUSE_PENDING:
                    case SERVICE_PAUSED:
                    default:
                        state = RDR_UNLOADED;
                        break;

                }
#if 0	//just check for load/unload state for now

                // go check the start/stop state
                if ( state == RDR_LOADED )
                {
                    BOOL IsOk;
                    HANDLE  DeviceHandle;
                    DWORD   BytesRet;
                    ULONG_PTR RdrStateValue;

                    DeviceHandle = CreateFile( DD_SMBMRX_USERMODE_DEV_NAME,
                                               GENERIC_READ | GENERIC_WRITE,
                                               FILE_SHARE_READ | FILE_SHARE_WRITE,
                                               NULL,
                                               OPEN_EXISTING,
                                               0,
                                               NULL );

                    if ( DeviceHandle != INVALID_HANDLE_VALUE )
                    {
                        DbgP((TEXT("Doing Get State DeviceIoControl\n")));
                        IsOk = DeviceIoControl( DeviceHandle,
                                                IOCTL_SMBMRX_GETSTATE,
                                                NULL,
                                                0,
                                                &RdrStateValue,
                                                sizeof(ULONG),
                                                &BytesRet,
                                                NULL );
                        CloseHandle( DeviceHandle );
                    }
                    if ( IsOk )
                    {
                        state = RdrStateValue;
                    }
                }
#endif
            }
            CloseServiceHandle( service );
        }
        CloseServiceHandle( sch );
    }

    return state;
}


ULONG_PTR RdrGetNextState( ULONG_PTR Action, ULONG_PTR CurrentState )
{
    return ActionsStatesArray[Action][CurrentState];
}


REGENTRY LinkageKeyValues[] =
{
    { TEXT("Bind"),     REG_MULTI_SZ,  0,   0 },
    { TEXT("Export"),   REG_MULTI_SZ,  0,   0 },
    { TEXT("Route"),    REG_MULTI_SZ,  0,   0 }
};

REGENTRY LinkageDisabledKeyValues[] =
{
    { TEXT("Bind"),     REG_MULTI_SZ,  0,   0 },
    { TEXT("Export"),   REG_MULTI_SZ,  0,   0 },
    { TEXT("Route"),    REG_MULTI_SZ,  0,   0 }
};


REGENTRY NetworkProviderKeyValues[] =
{
    {
        TEXT("Devicename"),
        REG_SZ,
        sizeof(SMBMRX_DEVICE_NAME),
        SMBMRX_DEVICE_NAME
    },
    {
        TEXT("ProviderPath"),
        REG_EXPAND_SZ,
        sizeof(PROVIDER_PATH),
        PROVIDER_PATH
    },
    {
        TEXT("Name"),
        REG_SZ,
        sizeof(SMBMRX_PROVIDER_NAME),
        SMBMRX_PROVIDER_NAME
    }
};


REGENTRY ProviderOrderKeyValues[] =
{
    { TEXT("ProviderOrder"), REG_SZ, 0,   0 }
};

BOOL RdrSetupServiceEntry( void )
/*++

Routine Description:

    This routine initializes the registry entries for the smbmrx
    minirdr. This only needs to be done once.

Arguments:

    None

Return Value:

   None

--*/
{
    HKEY hCurrentKey;
    SC_HANDLE sch, service;
    BOOL success = TRUE;

    DbgP(( TEXT( "Setting up ") RDRSERVICE TEXT(" registry Entries\n" ) ));
    sch = OpenSCManager( NULL, NULL, GENERIC_WRITE );
    if ( sch )
    {
        service = CreateService( sch,
                                 RDRSERVICE,
                                 RDRSERVICE,
                                 SERVICE_ALL_ACCESS,
                                 SERVICE_FILE_SYSTEM_DRIVER,
                                 SERVICE_DEMAND_START,
                                 SERVICE_ERROR_NORMAL,
                                 TEXT("System32\\DRIVERS\\") RDRSERVICE TEXT(".sys"),
                                 TEXT("Network"),
                                 NULL,
                                 NULL,
                                 NULL,
                                 NULL );


        if ( service )
        {
            CloseServiceHandle( service );
        }
        else if ( GetLastError( ) != ERROR_SERVICE_EXISTS )
        {
            success = FALSE;
        }
        CloseServiceHandle( sch );
    }
    else
    {
        success = FALSE;
    }


    // Read the linkage values associated with the Lanman workstation service.
    // This contains all the transports and the order in which they need to be used
    if ( success && OpenKey( WKSSERVICE_KEY TEXT("\\Linkage"), &hCurrentKey ) )
    {
        ULONG i;

        ReadRegistryKeyValues( hCurrentKey,
                               sizeof(LinkageKeyValues) / sizeof(REGENTRY),
                               LinkageKeyValues);
        RegCloseKey(hCurrentKey);
        // Update the SMB MRx linkage values
        if ( CreateKey( RDRSERVICE_KEY TEXT("\\Linkage"), &hCurrentKey ) )
        {
            WriteRegistryKeyValues( hCurrentKey,
                                    sizeof(LinkageKeyValues) / sizeof(REGENTRY),
                                    LinkageKeyValues);
            RegCloseKey(hCurrentKey);
        }
        else
        {
            success = FALSE;
        }
        for ( i = 0; i < ( sizeof(LinkageKeyValues) / sizeof(REGENTRY) ); i++ )
        {
            if (  LinkageKeyValues[i].pvValue )
            {
                free( LinkageKeyValues[i].pvValue );
                LinkageKeyValues[i].pvValue = NULL;
            }
        }
    }
    else
    {
        success = FALSE;
    }

    //if ( OpenKey( WKSSERVICE_KEY TEXT("\\Linkage\\Disabled",&hCurrentKey))
    //{
    //    ReadRegistryKeyValues( hCurrentKey,
    //                           sizeof(LinkageDisabledKeyValues) / sizeof(REGENTRY),
    //                           LinkageDisabledKeyValues);
    //    RegCloseKey(hCurrentKey);
    //}
    //else
    //{
    //    DbgP(( TEXT("Error Opening Key %s Status %d\n"),WKSSERVICE_KEY TEXT("\\Linkage\\Disabled"),GetLastError() ));
    //    return;
    //}

    // Update the SMB MRx linkage disabled values
    //if ( CreateKey( RDRSERVICE_KEY TEXT("\\Linkage\\Disabled") ,&hCurrentKey))
    //{
    //    WriteRegistryKeyValues( hCurrentKey,
    //                            sizeof(LinkageDisabledKeyValues)/sizeof(REGENTRY),
    //                            LinkageDisabledKeyValues );
    //    RegCloseKey(hCurrentKey);
    //}
    //else
    //{
    //    DbgP(( TEXT("Error Creating Key %s Status %d\n"),RDRSERVICE_KEY TEXT("\\linkage\\disabled",GetLastError() ));
    //    return;
    //}

    // Update the SMBmrx network provider section
    if ( success && CreateKey( RDRSERVICE_KEY TEXT("\\NetworkProvider"), &hCurrentKey ) )
    {
        WriteRegistryKeyValues( hCurrentKey,
                                sizeof(NetworkProviderKeyValues)/sizeof(REGENTRY),
                                NetworkProviderKeyValues);
        RegCloseKey(hCurrentKey);
    }
    else
    {
        success = FALSE;
    }


    if ( success && CreateKey( RDRSERVICE_KEY TEXT("\\Parameters"), &hCurrentKey ) )
    {
        RegCloseKey( hCurrentKey );
    }
    else
    {
        success = FALSE;
    }

    return success;
}

BOOL RdrSetupProviderOrder( void )
{
    LPTSTR pOrderString = NULL;
    ULONG_PTR len;
    BOOL success = TRUE;

    len = RdrGetProviderOrderString( &pOrderString ) * sizeof(TCHAR);
    if ( len > 0 && pOrderString )
    {
        if ( !RdrFindProviderInOrder( pOrderString, PROVIDER_NAME ) )
        {
            LPTSTR pNewOrderString;

            len += sizeof( PROVIDER_NAME ) + (2 * sizeof(TCHAR)); // add 2 for comma delimeter and null
            pNewOrderString = malloc( len );
            if ( pNewOrderString )
            {
                lstrcpy( pNewOrderString, pOrderString );
                lstrcat( pNewOrderString, TEXT(",") );
                lstrcat( pNewOrderString, PROVIDER_NAME );
                success = RdrSetProviderOrderString( pNewOrderString );
                free( pNewOrderString );
            }
        }
    }
    else
    {
        success = RdrSetProviderOrderString( PROVIDER_NAME );
    }
    if ( pOrderString )
    {
        free( pOrderString );
    }

    return success;
}


ULONG_PTR RdrGetProviderOrderString( LPTSTR *OrderString )
{
    HKEY hOrderKey;
    ULONG_PTR len = 0;

    if ( OpenKey( PROVIDER_ORDER_KEY, &hOrderKey ) )
    {
        ReadRegistryKeyValues( hOrderKey,
                               sizeof(ProviderOrderKeyValues) / sizeof(REGENTRY),
                               ProviderOrderKeyValues);

        RegCloseKey(hOrderKey);
        len = ProviderOrderKeyValues[0].dwLength / sizeof( TCHAR ) - 1;
        *OrderString = (LPTSTR) ProviderOrderKeyValues[0].pvValue;
    }

    return len;
}


BOOL RdrSetProviderOrderString( LPTSTR OrderString )
{
    HKEY hOrderKey;
    ULONG len = 0;
    BOOL rc = FALSE;

    if ( CreateKey( PROVIDER_ORDER_KEY, &hOrderKey ) )
    {
        ProviderOrderKeyValues[0].dwLength = ( lstrlen( OrderString ) + 1 ) * sizeof( TCHAR );
        ProviderOrderKeyValues[0].pvValue = OrderString;
        WriteRegistryKeyValues( hOrderKey,
                                sizeof(ProviderOrderKeyValues) / sizeof(REGENTRY),
                                ProviderOrderKeyValues);
        RegCloseKey(hOrderKey);

        rc = TRUE;
    }

    return rc;
}


BOOL RdrFindProviderInOrder( LPTSTR OrderString, LPTSTR Provider )
{
    LPTSTR pCompare;
    BOOL match = FALSE;

    if ( OrderString && Provider && *Provider )
    {
        pCompare = Provider;

        while ( *OrderString )
        {
            if ( toupper(*OrderString++) != toupper(*pCompare++) )
            {
                pCompare = Provider;
            }
            if ( *pCompare == TEXT('\0') )
            {
                if ( ( *OrderString == TEXT(',') ) || ( *OrderString == TEXT('\0') ) )
                {
                    match = TRUE;
                    break;
                }
                else    // hmm, it's a substring of another provider name
                {
                    while ( ( *OrderString != TEXT(',') ) && ( *OrderString != TEXT('\0') ) )
                    {
                        OrderString++;
                    }
                    pCompare = Provider;
                }
            }

        }
    }

    return match;
}



void
ReadRegistryKeyValues(
    HKEY       hCurrentKey,
    DWORD      NumberOfValues,
    PREGENTRY pValues)
/*++

Routine Description:

    This routine reads a bunch of values associated with a given key.

Arguments:

    hCurrentKey - the key

    NumberOfValues - the number of values

    pValues - the array of values

Return Value:

   None

--*/
{
    //
    // Iterate through table reading the values along the way
    //

    DWORD  i;

    for (i = 0; i < NumberOfValues; i++)
    {
        DWORD dwType;
        LPTSTR pszKey;

        dwType  = pValues[i].dwType;
        pszKey  = pValues[i].pszKey;

        switch (dwType)
        {
        case REG_SZ:
            GetRegsz(hCurrentKey, pszKey, &pValues[i].pvValue,
                     &pValues[i].dwLength);
            break;

        case REG_DWORD:
            GetRegdw(hCurrentKey, pszKey,  &pValues[i].pvValue,
                     &pValues[i].dwLength);
            break;

        case REG_EXPAND_SZ:
            GetRegesz(hCurrentKey, pszKey, &pValues[i].pvValue,
                      &pValues[i].dwLength);
            break;

        case REG_MULTI_SZ:
            GetRegmsz(hCurrentKey, pszKey, &pValues[i].pvValue,
                      &pValues[i].dwLength);
            break;

        case REG_BINARY:
            DbgP(( TEXT("%s is a REG_BINARY and won't be duplicated\n"), pszKey ));
            break;

        default:
            DbgP(( TEXT("%s is an unknown type; %d (decimal)\n"), pszKey, dwType ));
            break;

        }
    }
}

//
// Get a REG_SZ value and stick it in the table entry, along with the
// length
//

BOOL GetRegsz(HKEY hKey, LPTSTR pszKey, PVOID * ppvValue, DWORD *pdwLength)
{
    BYTE  achValue[1024];

    DWORD dwLength;
    LONG  Status;
    DWORD dwType   = REG_SZ;
    PBYTE pszValue = NULL;



    if ( (NULL == pszKey) || (NULL == ppvValue) ||
         (NULL == hKey)   || (NULL == pdwLength))
    {
        return FALSE;
    }

#ifdef _DEBUG
    FillMemory(achValue, sizeof(achValue), 0xcd);
#endif

    dwLength = sizeof(achValue);


    Status = RegQueryValueEx( hKey,
                               pszKey,
                               NULL,
                               &dwType,
                               (PUCHAR) &achValue[0],
                               &dwLength);

    if ((ERROR_SUCCESS != Status) || (REG_SZ != dwType) )
    {
        return FALSE;
    }

    pszValue = malloc(dwLength);

    if (NULL == pszValue)
    {
        return FALSE;
    }


    CopyMemory(pszValue, achValue, dwLength);

    *ppvValue  = pszValue;
    *pdwLength = dwLength;

    return TRUE;
}

//
// Get the value of a REG_EXPAND_SZ and its length
//

BOOL GetRegesz(HKEY hKey, LPTSTR pszKey, PVOID * ppvValue, DWORD * pdwLength)
{
    BYTE  achValue[1024];

    DWORD dwLength;
    LONG  Status;
    DWORD dwType   = REG_EXPAND_SZ;
    PBYTE pszValue = NULL;


    if ( (NULL == pszKey) || (NULL == ppvValue) ||
         (NULL == hKey)   || (NULL == pdwLength))
    {
        return FALSE;
    }

#ifdef _DEBUG
    FillMemory(achValue, sizeof(achValue), 0xcd);
#endif

    dwLength = sizeof(achValue);

    Status = RegQueryValueEx( hKey,
                               pszKey,
                               NULL,
                               &dwType,
                               (PUCHAR) &achValue[0],
                               &dwLength);

    if ((ERROR_SUCCESS != Status) || (REG_EXPAND_SZ != dwType))
    {
        return FALSE;
    }

    pszValue = malloc(dwLength);

    if (NULL == pszValue)
    {
        return FALSE;
    }

    CopyMemory(pszValue, achValue, dwLength);

    *ppvValue  = pszValue;
    *pdwLength = dwLength;

    return TRUE;
}


//
// Get value and length of REG_MULTI_SZ
//

BOOL GetRegmsz(HKEY hKey, LPTSTR pszKey, PVOID * ppvValue, DWORD * pdwLength)
{
    //BYTE  achValue[1024];
    BYTE  achValue[2048];	// careful, some of these strings are quite long

    DWORD dwLength;
    LONG  Status;
    DWORD dwType   = REG_MULTI_SZ;
    PBYTE pszValue = NULL;


    if ( (NULL == pszKey) || (NULL == ppvValue) ||
        (NULL == hKey)    || (NULL == pdwLength))
    {
        return FALSE;
    }

#ifdef _DEBUG
    FillMemory(achValue, sizeof(achValue), 0xcd);
#endif


    dwLength = sizeof(achValue);


    Status = RegQueryValueEx( hKey,
                               pszKey,
                               NULL,
                               &dwType,
                               (PUCHAR) &achValue[0],
                               &dwLength);

    if ((ERROR_SUCCESS != Status) || (REG_MULTI_SZ != dwType))
    {
        return FALSE;
    }

    pszValue = malloc(dwLength);

    if (NULL == pszValue)
    {
        return FALSE;
    }

    CopyMemory(pszValue, achValue, dwLength);

    *ppvValue  = pszValue;
    *pdwLength = dwLength;

    return TRUE;
}


//
// Get value and length of REG_DWORD
//


BOOL GetRegdw(HKEY hKey, LPTSTR pszKey, PVOID * ppvValue, DWORD * pdwLength)
{
    DWORD dwValue = 0;

    DWORD dwLength;
    LONG  Status;
    DWORD dwType   = REG_DWORD;



    if ( (NULL == pszKey) || (NULL == ppvValue) ||
         (NULL == hKey)   || (NULL == pdwLength) )
    {
        return FALSE;
    }

    dwLength = sizeof(dwValue);


    Status = RegQueryValueEx( hKey,
                               pszKey,
                               NULL,
                               &dwType,
                               (PUCHAR) &dwValue,
                               &dwLength);

    if ((ERROR_SUCCESS != Status) || (REG_DWORD != dwType))
    {
        return FALSE;
    }

    *ppvValue  = (PVOID) (ULONG_PTR) dwValue;
    *pdwLength = dwLength;

    return TRUE;
}



void
WriteRegistryKeyValues(
    HKEY        hCurrentKey,
    DWORD       NumberOfValues,
    PREGENTRY  pValues)
/*++

Routine Description:

    This routine reads a bunch of values associated with a given key.

Arguments:

    hCurrentKey - the key

    NumberOfValues - the number of values

    pValues - the array of values

Return Value:

   None

--*/
{
    DWORD i;


    for (i = 0; i < NumberOfValues; i++)
    {
        DWORD dwType;
        PVOID pvValue;
        DWORD dwLength;
        LPTSTR pszKey;

        pszKey   = pValues[i].pszKey;
        dwType   = pValues[i].dwType;
        dwLength = pValues[i].dwLength;
        pvValue  = pValues[i].pvValue;

        switch (dwType)
        {
        case REG_SZ:
            AddValue(hCurrentKey, pszKey, dwType, dwLength, pvValue);
            break;

        case REG_DWORD:
            AddValue(hCurrentKey, pszKey, dwType, dwLength, &pvValue);
            break;

        case REG_EXPAND_SZ:
            AddValue(hCurrentKey, pszKey, dwType, dwLength, pvValue);
            break;

        case REG_MULTI_SZ:
            AddValue(hCurrentKey, pszKey, dwType, dwLength, pvValue);
            break;

        case REG_BINARY:
            //
            // There are no binary values we need to copy. If we did, we'd
            // put something here
            //

            break;

        default:
            DbgP(( TEXT("%s is an unknown type; %d (decimal)\n"), pszKey, dwType ));
            break;

        }
    }
}

//
// Open a key so we can read the values
//


BOOL OpenKey(
    LPTSTR pszKey,
    PHKEY phKey)
/*++

Routine Description:

    This routine opens a registry key.

Arguments:

    pszKey - the name of the key relative to HKEY_LOCAL_MACHINE

    phKey - the key handlle

Return Value:

    TRUE if successful, otherwise FALSE

--*/
{
    HKEY  hNewKey = 0;
    DWORD Status;

    Status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                            pszKey,
                            0,
                            KEY_QUERY_VALUE,
                            &hNewKey);

    if (ERROR_SUCCESS != Status)
    {
        *phKey = NULL;
        return FALSE;
    }
    else
    {
        *phKey = hNewKey;
        return TRUE;
    }
}


BOOL CreateKey(LPTSTR pszKey, PHKEY phKey)
/*++

Routine Description:

    This routine creates a registry key.

Arguments:

    pszKey - the name of the key relative to HKEY_LOCAL_MACHINE

    phKey - the key handlle

Return Value:

    TRUE if successful, otherwise FALSE

--*/
{
    LONG   Status;
    DWORD  Disposition;

    Status =  RegCreateKeyEx( HKEY_LOCAL_MACHINE,
                               pszKey,
                               0,
                               REG_NONE,
                               REG_OPTION_NON_VOLATILE,
                               KEY_ALL_ACCESS,
                               NULL,
                               phKey,
                               &Disposition);

    if ( ERROR_SUCCESS == Status)
    {
        return TRUE;
    }
    else
    {
        DbgP(( TEXT("error creating key %s Status %d\n"), pszKey, Status ));
        return FALSE;
    }
}


//
// Add a value to the registry
//


BOOL AddValue(HKEY hKey, LPTSTR pszKey, DWORD dwType, DWORD dwLength, PVOID pvValue)
{

    BOOL fSuccess = TRUE;
    LONG Status   = ERROR_SUCCESS;
    HANDLE th;

    Status = RegSetValueEx( hKey,
                             pszKey,
                             0,
                             dwType,
                             pvValue,
                             dwLength);


    if (Status != ERROR_SUCCESS)
    {
        fSuccess = FALSE;
        //RegCloseKey(hKey);
    }

    return fSuccess;
}


// Format and write debug information to OutputDebugString
ULONG
_cdecl
DbgPrint(
    LPTSTR Format,
    ...
    )
{
    ULONG rc = 0;
    TCHAR szbuffer[255];

    va_list marker;
    va_start( marker, Format );
    {
         rc = wvsprintf( szbuffer, Format, marker );
         OutputDebugString( TRACE_TAG );
         OutputDebugString( szbuffer );
    }

    return rc;
}
