README.TXT

The \Mssdk\Samples\WinBase directory contains samples for Windows
base services including:

* Cluster
* Fibers
* io
* ipc
* memory
* Registry
* Smart Cards
* Security
* Windows NT Media Services (Windows NT 5.0)

The build environment (headers, libraries, and minimum build
tools) for the Platform SDK must be installed in order to build
any of the samples in the \Mssdk\Samples directory. The build
environment can be installed from the readme.htm or by running
setup.exe on the root of the Platform SDK compact disc. The most
recent version of the build environment is also available at
http://www.microsoft.com/msdn/sdk/platform.htm.

Some components may also require you to install runtimes, depending
on the platform on which you are trying to run the sample.
Redistributables are available in the \Mssdk\Redist subdirectory
on the Platform SDK compact disc.

Documentation for the components listed above can be installed
from the Platform SDK compact disc, or can be viewed online at
http://premium.microsoft.com/msdn/library/. This is a free service;
registration is necessary.

If you do not see any subdirectories in the \Mssdk\Samples\WinBase
node, you'll need to install the COM and ActiveX components
from the Platform SDK compact disc or download and install them
from http://www.microsoft.com/msdn/sdk/platform.htm.


README.TXT

Microsoft Cluster Server SDK Final Release Notes

This document is intended to provide additional information
about the Microsoft Cluster Server Software SDK. The
information presented here supplements or replaces the
information in the printed documentation.

1.0 Software Issues

  1.1  This SDK is only supported with Visual C++ 4.2b and 5.0.

  1.2  If this SDK is installed for use with Visuall C++ 4.2b, the
       Active Template Library (ATL) version 2.0 must be installed
       in order to build the AppWizard-generated and sample Cluster
       Administrator extensions. ATL 2.0 is the latest version of ATL
       usable with Visual C++ 4.2b and is provided with this SDK. If
       this SDK is installed for use with Visual C++ 5.0, the Active
       Template Library (ATL) version 2.1 will already be installed
       and can be used as is.


READRES.TXT

========================================================================
       CLUSTER RESOURCE DLL : SMB Sample (SmbSmp)
========================================================================

AppWizard has created this SMB Sample resource DLL for you.  This
DLL not only demonstrates the basics of using the Cluster API but is
also a starting point for writing your DLL.

This file contains a summary of what you will find in each of the files
that make up your SMB Sample resource DLL.

SmbSmp.c
    This is the main DLL source file.  It contains implementations for
    all required resource DLL entry points along with some helper
    functions.

SmbSmp.rc
    This is a listing of all of the Microsoft Windows resources that
    the program uses.  This file can be directly edited in Microsoft
    Developer Studio.

SmbSmp.def
    This file contains information about the DLL that must be
    provided to run with the cluster software.  It defines parameters
    such as the name and description of the DLL.  It also exports
    functions from the DLL.

/////////////////////////////////////////////////////////////////////////////
Adding your resource type to the cluster:

To add your resource type to the cluster, use cluster.exe.

    Cluster resourcetype "SMB Sample" /create /dllname:SmbSmp.dll

If you don't specify a path to your resource DLL, Cluster Server will search
for it first in the cluster directory and then on the system path.

You can now create resources of type "SMB Sample" using either
Cluster Administrator or cluster.exe.  To use Cluster Administrator to
create resources, create a Cluster Administrator Extension DLL for your
resource.  This is highly recommended.  To create a resource using cluster.exe,
use a command like the following:

    cluster resource "My SMB Sample" /create /group:"My Group" /type:"SMB Sample"
    cluster resource "My SMB Sample" /privproperties
        ShareName="value"
        Path="value"

/////////////////////////////////////////////////////////////////////////////
Other notes:

AppWizard uses "TODO:" to indicate parts of the source code you should add
to or customize.

AppWizard uses "ADDPARAM:" to indicate parts of the source code you should
modify if you add or remove parameters.

/////////////////////////////////////////////////////////////////////////////


SMBSMP.C

/*++

Copyright (c) 1997 <company name>

Module Name:

    SmbSmp.c

Abstract:

    Resource DLL for SMB Sample (SmbSmp).

Author:

    <user> (<email-name>) Mmmm DD, 1997

Revision History:

--*/

#pragma comment(lib, "clusapi.lib")
#pragma comment(lib, "resutils.lib")
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "netapi32.lib")

#define UNICODE 1

#pragma warning( disable : 4115 )  // named type definition in parentheses
#pragma warning( disable : 4201 )  // nonstandard extension used : nameless struct/union
#pragma warning( disable : 4214 )  // nonstandard extension used : bit field types other than int

#include <windows.h>

#pragma warning( default : 4214 )  // nonstandard extension used : bit field types other than int
#pragma warning( default : 4201 )  // nonstandard extension used : nameless struct/union
#pragma warning( default : 4115 )  // named type definition in parentheses

#include <clusapi.h>
#include <resapi.h>
#include <stdio.h>
#include <lm.h>
#include <lmerr.h>

//
// Type and constant definitions.
//

#define SMBSMP_RESNAME  L"SMB Sample"
#define SMBSMP_SVCNAME  TEXT("LanmanServer")

#define DBG_PRINT printf

// ADDPARAM: Add new parameters here.
#define PARAM_NAME__SHARENAME L"ShareName"
#define PARAM_NAME__PATH L"Path"
#define PARAM_NAME__REMARK L"Remark"

// ADDPARAM: Add new parameters here.
typedef struct _SMBSMP_PARAMS {
    PWSTR           ShareName;
    PWSTR           Path;
    PWSTR           Remark;
} SMBSMP_PARAMS, *PSMBSMP_PARAMS;

typedef struct _SMBSMP_RESOURCE {
    RESID                   ResId; // for validation
    SMBSMP_PARAMS         Params;
    HKEY                    ParametersKey;
    RESOURCE_HANDLE         ResourceHandle;
    LPWSTR                  ResourceName;
    CLUS_WORKER             OnlineThread;
    CLUSTER_RESOURCE_STATE  State;
} SMBSMP_RESOURCE, *PSMBSMP_RESOURCE;

//
// Global data.
//

// Event Logging routine.

PLOG_EVENT_ROUTINE g_LogEvent = NULL;

// Resource Status routine for pending Online and Offline calls.

PSET_RESOURCE_STATUS_ROUTINE g_SetResourceStatus = NULL;

// Forward reference to our RESAPI function table.

extern CLRES_FUNCTION_TABLE g_SmbSmpFunctionTable;

//
// SMB Sample resource read-write private properties.
//
RESUTIL_PROPERTY_ITEM
SmbSmpResourcePrivateProperties[] = {
    { PARAM_NAME__SHARENAME, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(SMBSMP_PARAMS,ShareName) },
    { PARAM_NAME__PATH, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(SMBSMP_PARAMS,Path) },
    { PARAM_NAME__REMARK, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET(SMBSMP_PARAMS,Remark) },
    { 0 }
};

//
// Function prototypes.
//

DWORD
WINAPI
Startup(
    IN LPCWSTR ResourceType,
    IN DWORD MinVersionSupported,
    IN DWORD MaxVersionSupported,
    IN PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus,
    IN PLOG_EVENT_ROUTINE LogEvent,
    OUT PCLRES_FUNCTION_TABLE *FunctionTable
    );

RESID
WINAPI
SmbSmpOpen(
    IN LPCWSTR ResourceName,
    IN HKEY ResourceKey,
    IN RESOURCE_HANDLE ResourceHandle
    );

VOID
WINAPI
SmbSmpClose(
    IN RESID ResourceId
    );

DWORD
WINAPI
SmbSmpOnline(
    IN RESID ResourceId,
    IN OUT PHANDLE EventHandle
    );

DWORD
WINAPI
SmbSmpOnlineThread(
    PCLUS_WORKER WorkerPtr,
    IN PSMBSMP_RESOURCE ResourceEntry
    );

DWORD
WINAPI
SmbSmpOffline(
    IN RESID ResourceId
    );

VOID
WINAPI
SmbSmpTerminate(
    IN RESID ResourceId
    );

DWORD
SmbSmpDoTerminate(
    IN PSMBSMP_RESOURCE ResourceEntry
    );

BOOL
WINAPI
SmbSmpLooksAlive(
    IN RESID ResourceId
    );

BOOL
WINAPI
SmbSmpIsAlive(
    IN RESID ResourceId
    );

BOOL
SmbSmpCheckIsAlive(
    IN PSMBSMP_RESOURCE ResourceEntry
    );

DWORD
WINAPI
SmbSmpResourceControl(
    IN RESID ResourceId,
    IN DWORD ControlCode,
    IN PVOID InBuffer,
    IN DWORD InBufferSize,
    OUT PVOID OutBuffer,
    IN DWORD OutBufferSize,
    OUT LPDWORD BytesReturned
    );

DWORD
SmbSmpGetPrivateResProperties(
    IN OUT PSMBSMP_RESOURCE ResourceEntry,
    OUT PVOID OutBuffer,
    IN DWORD OutBufferSize,
    OUT LPDWORD BytesReturned
    );

DWORD
SmbSmpValidatePrivateResProperties(
    IN OUT PSMBSMP_RESOURCE ResourceEntry,
    IN const PVOID InBuffer,
    IN DWORD InBufferSize,
    OUT PSMBSMP_PARAMS Params
    );

DWORD
SmbSmpSetPrivateResProperties(
    IN OUT PSMBSMP_RESOURCE ResourceEntry,
    IN const PVOID InBuffer,
    IN DWORD InBufferSize
    );

BOOLEAN
WINAPI
DllMain(
    IN HINSTANCE    DllHandle,
    IN DWORD        Reason,
    IN LPVOID       Reserved
    )

/*++

Routine Description:

    Main DLL entry point.

Arguments:

    DllHandle - DLL instance handle.

    Reason - Reason for being called.

    Reserved - Reserved argument.

Return Value:

    TRUE - Success.

    FALSE - Failure.

--*/

{
    switch( Reason ) {

    case DLL_PROCESS_ATTACH:
        DisableThreadLibraryCalls( DllHandle );
        break;

    case DLL_PROCESS_DETACH:
        break;
    }

    return(TRUE);

} // DllMain

DWORD
WINAPI
Startup(
    IN LPCWSTR ResourceType,
    IN DWORD MinVersionSupported,
    IN DWORD MaxVersionSupported,
    IN PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus,
    IN PLOG_EVENT_ROUTINE LogEvent,
    OUT PCLRES_FUNCTION_TABLE *FunctionTable
    )

/*++

Routine Description:

    Startup the resource DLL. This routine verifies that at least one
    currently supported version of the resource DLL is between
    MinVersionSupported and MaxVersionSupported. If not, then the resource
    DLL should return ERROR_REVISION_MISMATCH.

    If more than one version of the resource DLL interface is supported by
    the resource DLL, then the highest version (up to MaxVersionSupported)
    should be returned as the resource DLL's interface. If the returned
    version is not within range, then startup fails.

    The ResourceType is passed in so that if the resource DLL supports more
    than one ResourceType, it can pass back the correct function table
    associated with the ResourceType.

Arguments:

    ResourceType - The type of resource requesting a function table.

    MinVersionSupported - The minimum resource DLL interface version
        supported by the cluster software.

    MaxVersionSupported - The maximum resource DLL interface version
        supported by the cluster software.

    SetResourceStatus - Pointer to a routine that the resource DLL should
        call to update the state of a resource after the Online or Offline
        routine returns a status of ERROR_IO_PENDING.

    LogEvent - Pointer to a routine that handles the reporting of events
        from the resource DLL.

    FunctionTable - Returns a pointer to the function table defined for the
        version of the resource DLL interface returned by the resource DLL.

Return Value:

    ERROR_SUCCESS - The operation was successful.

    ERROR_MOD_NOT_FOUND - The resource type is unknown by this DLL.

    ERROR_REVISION_MISMATCH - The version of the cluster service doesn't
        match the versrion of the DLL.

    Win32 error code - The operation failed.

--*/

{
    if ( (MinVersionSupported > CLRES_VERSION_V1_00) ||
         (MaxVersionSupported < CLRES_VERSION_V1_00) ) {
        return(ERROR_REVISION_MISMATCH);
    }

    if ( lstrcmpiW( ResourceType, SMBSMP_RESNAME ) != 0 ) {
        return(ERROR_MOD_NOT_FOUND);
    }

    if ( !g_LogEvent ) {
        g_LogEvent = LogEvent;
        g_SetResourceStatus = SetResourceStatus;
    }

    *FunctionTable = &g_SmbSmpFunctionTable;

    return(ERROR_SUCCESS);

} // Startup

RESID
WINAPI
SmbSmpOpen(
    IN LPCWSTR ResourceName,
    IN HKEY ResourceKey,
    IN RESOURCE_HANDLE ResourceHandle
    )

/*++

Routine Description:

    Open routine for SMB Sample resources.

    Open the specified resource (create an instance of the resource).
    Allocate all structures necessary to bring the specified resource
    online.

Arguments:

    ResourceName - Supplies the name of the resource to open.

    ResourceKey - Supplies handle to the resource's cluster configuration
        database key.

    ResourceHandle - A handle that is passed back to the resource monitor
        when the SetResourceStatus or LogEvent method is called. See the
        description of the SetResourceStatus and LogEvent methods on the
        SmbSmpStatup routine. This handle should never be closed or used
        for any purpose other than passing it as an argument back to the
        Resource Monitor in the SetResourceStatus or LogEvent callback.

Return Value:

    RESID of created resource.

    NULL on failure.

--*/

{
    DWORD               status;
    DWORD               disposition;
    RESID               resid = 0;
    HKEY                parametersKey = NULL;
    PSMBSMP_RESOURCE resourceEntry = NULL;

    //
    // Open the Parameters registry key for this resource.
    //

    status = ClusterRegCreateKey( ResourceKey,
                                  L"Parameters",
                                  0,
                                  KEY_ALL_ACCESS,
                                  NULL,
                                  &parametersKey,
                                  &disposition );

    if ( status != ERROR_SUCCESS ) {
        (g_LogEvent)(
            ResourceHandle,
            LOG_ERROR,
            L"Unable to open Parameters key. Error: %1!u!.\n",
            status );
        goto exit;
    }

    //
    // Allocate a resource entry.
    //

    resourceEntry = (PSMBSMP_RESOURCE) LocalAlloc( LMEM_FIXED, sizeof(SMBSMP_RESOURCE) );

    if ( resourceEntry == NULL ) {
        status = GetLastError();
        (g_LogEvent)(
            ResourceHandle,
            LOG_ERROR,
            L"Unable to allocate resource entry structure. Error: %1!u!.\n",
            status );
        goto exit;
    }

    //
    // Initialize the resource entry..
    //

    ZeroMemory( resourceEntry, sizeof(SMBSMP_RESOURCE) );

    resourceEntry->ResId = (RESID)resourceEntry; // for validation
    resourceEntry->ResourceHandle = ResourceHandle;
    resourceEntry->ParametersKey = parametersKey;
    resourceEntry->State = ClusterResourceOffline;

    //
    // Save the name of the resource.
    //
    resourceEntry->ResourceName = LocalAlloc( LMEM_FIXED, (lstrlenW( ResourceName ) + 1) * sizeof(WCHAR) );
    if ( resourceEntry->ResourceName == NULL ) {
        goto exit;
    }
    lstrcpyW( resourceEntry->ResourceName, ResourceName );

    //
    // Startup for the resource.
    //
    // TODO: Add your resource startup code here.

    resid = (RESID)resourceEntry;

exit:

    if ( resid == 0 ) {
        if ( parametersKey != NULL ) {
            ClusterRegCloseKey( parametersKey );
        }
        if ( resourceEntry != NULL ) {
            LocalFree( resourceEntry->ResourceName );
            LocalFree( resourceEntry );
        }
    }

    if ( status != ERROR_SUCCESS ) {
        SetLastError( status );
    }

    return(resid);

} // SmbSmpOpen

VOID
WINAPI
SmbSmpClose(
    IN RESID ResourceId
    )

/*++

Routine Description:

    Close routine for SMB Sample resources.

    Close the specified resource and deallocate all structures, etc.,
    allocated in the Open call. If the resource is not in the offline state,
    then the resource should be taken offline (by calling Terminate) before
    the close operation is performed.

Arguments:

    ResourceId - Supplies the RESID of the resource to close.

Return Value:

    None.

--*/

{
    PSMBSMP_RESOURCE resourceEntry;

    resourceEntry = (PSMBSMP_RESOURCE)ResourceId;

    if ( resourceEntry == NULL ) {
        DBG_PRINT( "SmbSmp: Close request for a nonexistent resource id %u\n",
                   ResourceId );
        return;
    }

    if ( resourceEntry->ResId != ResourceId ) {
        (g_LogEvent)(
            resourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Close resource sanity check failed! ResourceId = %1!u!.\n",
            ResourceId );
        return;
    }

#ifdef LOG_VERBOSE
    (g_LogEvent)(
        resourceEntry->ResourceHandle,
        LOG_INFORMATION,
        L"Close request.\n" );
#endif

    //
    // Close the Parameters key.
    //

    if ( resourceEntry->ParametersKey ) {
        ClusterRegCloseKey( resourceEntry->ParametersKey );
    }

    //
    // Deallocate the resource entry.
    //

    // ADDPARAM: Add new parameters here.
    LocalFree( resourceEntry->Params.ShareName );
    LocalFree( resourceEntry->Params.Path );
    LocalFree( resourceEntry->Params.Remark );

    LocalFree( resourceEntry->ResourceName );
    LocalFree( resourceEntry );

} // SmbSmpClose

DWORD
WINAPI
SmbSmpOnline(
    IN RESID ResourceId,
    IN OUT PHANDLE EventHandle
    )

/*++

Routine Description:

    Online routine for SMB Sample resources.

    Bring the specified resource online (available for use). The resource
    DLL should attempt to arbitrate for the resource if it is present on a
    shared medium, like a shared SCSI bus.

Arguments:

    ResourceId - Supplies the resource id for the resource to be brought
        online (available for use).

    EventHandle - Returns a signalable handle that is signaled when the
        resource DLL detects a failure on the resource. This argument is
        NULL on input, and the resource DLL returns NULL if asynchronous
        notification of failures is not supported, otherwise this must be
        the address of a handle that is signaled on resource failures.

Return Value:

    ERROR_SUCCESS - The operation was successful, and the resource is now
        online.

    ERROR_RESOURCE_NOT_FOUND - RESID is not valid.

    ERROR_RESOURCE_NOT_AVAILABLE - If the resource was arbitrated with some
        other systems and one of the other systems won the arbitration.

    ERROR_IO_PENDING - The request is pending, a thread has been activated
        to process the online request. The thread that is processing the
        online request will periodically report status by calling the
        SetResourceStatus callback method, until the resource is placed into
        the ClusterResourceOnline state (or the resource monitor decides to
        timeout the online request and Terminate the resource. This pending
        timeout value is settable and has a default value of 3 minutes.).

    Win32 error code - The operation failed.

--*/

{
    PSMBSMP_RESOURCE resourceEntry = NULL;
    DWORD               status;

    resourceEntry = (PSMBSMP_RESOURCE)ResourceId;

    if ( resourceEntry == NULL ) {
        DBG_PRINT( "SmbSmp: Online request for a nonexistent resource id %u.\n",
                   ResourceId );
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    if ( resourceEntry->ResId != ResourceId ) {
        (g_LogEvent)(
            resourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Online service sanity check failed! ResourceId = %1!u!.\n",
            ResourceId );
        return(ERROR_RESOURCE_NOT_FOUND);
    }

#ifdef LOG_VERBOSE
    (g_LogEvent)(
        resourceEntry->ResourceHandle,
        LOG_INFORMATION,
        L"Online request.\n" );
#endif

    resourceEntry->State = ClusterResourceOffline;
    ClusWorkerTerminate( &resourceEntry->OnlineThread );
    status = ClusWorkerCreate( &resourceEntry->OnlineThread,
                               SmbSmpOnlineThread,
                               resourceEntry );
    if ( status != ERROR_SUCCESS ) {
        resourceEntry->State = ClusterResourceFailed;
        (g_LogEvent)(
            resourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Online: Unable to start thread, status %1!u!.\n",
            status
            );
    } else {
        status = ERROR_IO_PENDING;
    }

    return(status);

} // SmbSmpOnline

DWORD
WINAPI
SmbSmpOnlineThread(
    PCLUS_WORKER WorkerPtr,
    IN PSMBSMP_RESOURCE ResourceEntry
    )

/*++

Routine Description:

    Worker function which brings a resource from the resource table online.
    This function is executed in a separate thread.

Arguments:

    WorkerPtr - Supplies the worker structure

    ResourceEntry - A pointer to the SMBSMP_RESOURCE block for this resource.

Returns:

    ERROR_SUCCESS - The operation completed successfully.

    Win32 error code - The operation failed.

--*/

{
    RESOURCE_STATUS     resourceStatus;
    DWORD               status;
    LPWSTR              nameOfPropInError;
    SHARE_INFO_2        shareInfo;

    resourceStatus.ResourceState = ClusterResourceFailed;
    resourceStatus.WaitHint = 0;
    resourceStatus.CheckPoint = 1;

    //
    // Read parameters.
    //
    status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ParametersKey,
                                                   SmbSmpResourcePrivateProperties,
                                                   (LPBYTE) &ResourceEntry->Params,
                                                   TRUE, // CheckForRequiredProperties
                                                   &nameOfPropInError );
    if ( status != ERROR_SUCCESS ) {
        (g_LogEvent)(
            ResourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Unable to read the '%1' property. Error: %2!u!.\n",
            (nameOfPropInError == NULL ? L"" : nameOfPropInError),
            status );
        goto exit;
    }

    //
    // Start the SmbSmp service
    //
    status = ResUtilStartResourceService( SMBSMP_SVCNAME, NULL );
    if ( status == ERROR_SERVICE_ALREADY_RUNNING ) {
        status = ERROR_SUCCESS;
    } else if ( status != ERROR_SUCCESS ) {
        goto exit;
    }

    //
    // Bring the resource online.
    //
    // TODO: Add code to bring your resource online.
    if ( status == ERROR_SUCCESS ) {
        shareInfo.shi2_netname =      ResourceEntry->Params.ShareName;
        shareInfo.shi2_type =         STYPE_DISKTREE;
        shareInfo.shi2_remark =       ResourceEntry->Params.Remark;
        shareInfo.shi2_permissions =  ACCESS_ALL;
        shareInfo.shi2_max_uses =     (DWORD)-1;
        shareInfo.shi2_current_uses = 0;
        shareInfo.shi2_path =         ResourceEntry->Params.Path;
        shareInfo.shi2_passwd =       NULL;

        status = NetShareAdd( NULL, 2, (PBYTE)&shareInfo, NULL );
        if ( status != ERROR_SUCCESS ) {
            (g_LogEvent)(
                ResourceEntry->ResourceHandle,
                LOG_ERROR,
                L"Error creating share '%1'. Error: %2!u!.\n",
                ResourceEntry->Params.ShareName,
                status );
            goto exit;
        }
    }

exit:
    if ( status != ERROR_SUCCESS ) {
        (g_LogEvent)(
            ResourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Error %1!u! bringing resource online.\n",
            status );
    } else {
        resourceStatus.ResourceState = ClusterResourceOnline;
    }

    // _ASSERTE(g_SetResourceStatus != NULL);
    g_SetResourceStatus( ResourceEntry->ResourceHandle, &resourceStatus );
    ResourceEntry->State = resourceStatus.ResourceState;

    return(status);

} // SmbSmpOnlineThread

DWORD
WINAPI
SmbSmpOffline(
    IN RESID ResourceId
    )

/*++

Routine Description:

    Offline routine for SMB Sample resources.

    Take the specified resource offline gracefully (unavailable for use).
    Wait for any cleanup operations to complete before returning.

Arguments:

    ResourceId - Supplies the resource id for the resource to be shutdown
        gracefully.

Return Value:

    ERROR_SUCCESS - The request completed successfully and the resource is
        offline.

    ERROR_RESOURCE_NOT_FOUND - RESID is not valid.

    ERROR_IO_PENDING - The request is still pending, a thread has been
        activated to process the offline request. The thread that is
        processing the offline will periodically report status by calling
        the SetResourceStatus callback method, until the resource is placed
        into the ClusterResourceOffline state (or the resource monitor decides
        to timeout the offline request and Terminate the resource).

    Win32 error code - Will cause the resource monitor to log an event and
        call the Terminate routine.

--*/

{
    PSMBSMP_RESOURCE resourceEntry;

    resourceEntry = (PSMBSMP_RESOURCE)ResourceId;

    if ( resourceEntry == NULL ) {
        DBG_PRINT( "SmbSmp: Offline request for a nonexistent resource id %u\n",
            ResourceId );
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    if ( resourceEntry->ResId != ResourceId ) {
        (g_LogEvent)(
            resourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Offline resource sanity check failed! ResourceId = %1!u!.\n",
            ResourceId );
        return(ERROR_RESOURCE_NOT_FOUND);
    }

#ifdef LOG_VERBOSE
    (g_LogEvent)(
        resourceEntry->ResourceHandle,
        LOG_INFORMATION,
        L"Offline request.\n" );
#endif

    // TODO: Offline code

    // NOTE: Offline should try to shut the resource down gracefully, whereas
    // Terminate must shut the resource down immediately. If there are no
    // differences between a graceful shut down and an immediate shut down,
    // Terminate can be called for Offline, as it is below.  However, if there
    // are differences, replace the call to Terminate below with your graceful
    // shutdown code.

    //
    // Terminate the resource.
    //
    return SmbSmpDoTerminate( resourceEntry );

} // SmbSmpOffline

VOID
WINAPI
SmbSmpTerminate(
    IN RESID ResourceId
    )

/*++

Routine Description:

    Terminate routine for SMB Sample resources.

    Take the specified resource offline immediately (the resource is
    unavailable for use).

Arguments:

    ResourceId - Supplies the resource id for the resource to be brought
        offline.

Return Value:

    None.

--*/

{
    PSMBSMP_RESOURCE resourceEntry;

    resourceEntry = (PSMBSMP_RESOURCE)ResourceId;

    if ( resourceEntry == NULL ) {
        DBG_PRINT( "SmbSmp: Terminate request for a nonexistent resource id %u\n",
            ResourceId );
        return;
    }

    if ( resourceEntry->ResId != ResourceId ) {
        (g_LogEvent)(
            resourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Terminate resource sanity check failed! ResourceId = %1!u!.\n",
            ResourceId );
        return;
    }

#ifdef LOG_VERBOSE
    (g_LogEvent)(
        resourceEntry->ResourceHandle,
        LOG_INFORMATION,
        L"Terminate request.\n" );
#endif

    //
    // Terminate the resource.
    //
    SmbSmpDoTerminate( resourceEntry );
    resourceEntry->State = ClusterResourceOffline;

} // SmbSmpTerminate

DWORD
SmbSmpDoTerminate(
    IN PSMBSMP_RESOURCE ResourceEntry
    )

/*++

Routine Description:

    Do the actual Terminate work for SMB Sample resources.

Arguments:

    ResourceEntry - Supplies resource entry for resource to be terminated

Return Value:

    ERROR_SUCCESS - The request completed successfully and the resource is
        offline.

    Win32 error code - Will cause the resource monitor to log an event and
        call the Terminate routine.

--*/

{
    DWORD       status = ERROR_SUCCESS;

    //
    // Kill off any pending threads.
    //
    ClusWorkerTerminate( &ResourceEntry->OnlineThread );

    //
    // Terminate the resource.
    //
    // TODO: Add code to terminate your resource.
    status = NetShareDel( NULL, ResourceEntry->Params.ShareName, 0 );

    if ( status != NO_ERROR ) {
        (g_LogEvent)(
            ResourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Error removing share '%1'. Error %2!u!.\n",
            ResourceEntry->Params.ShareName,
            status );
    } else {
        ResourceEntry->State = ClusterResourceOffline;
    }

    return(status);

} // SmbSmpDoTerminate

BOOL
WINAPI
SmbSmpLooksAlive(
    IN RESID ResourceId
    )

/*++

Routine Description:

    LooksAlive routine for SMB Sample resources.

    Perform a quick check to determine if the specified resource is probably
    online (available for use).  This call should not block for more than
    300 ms, preferably less than 50 ms.

Arguments:

    ResourceId - Supplies the resource id for the resource to polled.

Return Value:

    TRUE - The specified resource is probably online and available for use.

    FALSE - The specified resource is not functioning normally.

--*/

{
    PSMBSMP_RESOURCE  resourceEntry;

    resourceEntry = (PSMBSMP_RESOURCE)ResourceId;

    if ( resourceEntry == NULL ) {
        DBG_PRINT("SmbSmp: LooksAlive request for a nonexistent resource id %u\n",
            ResourceId );
        return(FALSE);
    }

    if ( resourceEntry->ResId != ResourceId ) {
        (g_LogEvent)(
            resourceEntry->ResourceHandle,
            LOG_ERROR,
            L"LooksAlive sanity check failed! ResourceId = %1!u!.\n",
            ResourceId );
        return(FALSE);
    }

#ifdef LOG_VERBOSE
    (g_LogEvent)(
        resourceEntry->ResourceHandle,
        LOG_INFORMATION,
        L"LooksAlive request.\n" );
#endif

    // TODO: LooksAlive code

    // NOTE: LooksAlive should be a quick check to see if the resource is
    // available or not, whereas IsAlive should be a thorough check.  If
    // there are no differences between a quick check and a thorough check,
    // IsAlive can be called for LooksAlive, as it is below.  However, if there
    // are differences, replace the call to IsAlive below with your quick
    // check code.

    //
    // Check to see if the resource is alive.
    //
    return(SmbSmpCheckIsAlive( resourceEntry ));

} // SmbSmpLooksAlive

BOOL
WINAPI
SmbSmpIsAlive(
    IN RESID ResourceId
    )

/*++

Routine Description:

    IsAlive routine for SMB Sample resources.

    Perform a thorough check to determine if the specified resource is online
    (available for use). This call should not block for more than 400 ms,
    preferably less than 100 ms.

Arguments:

    ResourceId - Supplies the resource id for the resource to polled.

Return Value:

    TRUE - The specified resource is online and functioning normally.

    FALSE - The specified resource is not functioning normally.

--*/

{
    PSMBSMP_RESOURCE  resourceEntry;

    resourceEntry = (PSMBSMP_RESOURCE)ResourceId;

    if ( resourceEntry == NULL ) {
        DBG_PRINT("SmbSmp: IsAlive request for a nonexistent resource id %u\n",
            ResourceId );
        return(FALSE);
    }

    if ( resourceEntry->ResId != ResourceId ) {
        (g_LogEvent)(
            resourceEntry->ResourceHandle,
            LOG_ERROR,
            L"IsAlive sanity check failed! ResourceId = %1!u!.\n",
            ResourceId );
        return(FALSE);
    }

#ifdef LOG_VERBOSE
    (g_LogEvent)(
        resourceEntry->ResourceHandle,
        LOG_INFORMATION,
        L"IsAlive request.\n" );
#endif

    //
    // Check to see if the resource is alive.
    //
    return(SmbSmpCheckIsAlive( resourceEntry ));

} // SmbSmpIsAlive

BOOL
SmbSmpCheckIsAlive(
    IN PSMBSMP_RESOURCE ResourceEntry
    )

/*++

Routine Description:

    Check to see if the resource is alive for SMB Sample resources.

Arguments:

    ResourceEntry - Supplies the resource entry for the resource to polled.

Return Value:

    TRUE - The specified resource is online and functioning normally.

    FALSE - The specified resource is not functioning normally.

--*/

{
    SHARE_INFO_2        shareInfo;
    DWORD               status;

    //
    // Check to see if the resource is alive.
    //
    // TODO: Add code to determine if your resource is alive.
    status = NetShareGetInfo( NULL,
                              ResourceEntry->Params.ShareName,
                              2, // return a SHARE_INFO_2 structure
                              (LPBYTE *) &shareInfo );

    if ( status == NERR_NetNameNotFound ) {
        (g_LogEvent)(
            ResourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Error, share '%1' went away.\n",
            ResourceEntry->Params.ShareName );
        return(FALSE);
    } else if ( status != ERROR_SUCCESS ) {
        (g_LogEvent)(
            ResourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Error checking for share '%1'. Error %2!u!.\n",
            ResourceEntry->Params.ShareName,
            status );
    }

    return(TRUE);

} // SmbSmpCheckIsAlive

DWORD
WINAPI
SmbSmpResourceControl(
    IN RESID ResourceId,
    IN DWORD ControlCode,
    IN PVOID InBuffer,
    IN DWORD InBufferSize,
    OUT PVOID OutBuffer,
    IN DWORD OutBufferSize,
    OUT LPDWORD BytesReturned
    )

/*++

Routine Description:

    ResourceControl routine for SMB Sample resources.

    Perform the control request specified by ControlCode on the specified
    resource.

Arguments:

    ResourceId - Supplies the resource id for the specific resource.

    ControlCode - Supplies the control code that defines the action
        to be performed.

    InBuffer - Supplies a pointer to a buffer containing input data.

    InBufferSize - Supplies the size, in bytes, of the data pointed
        to by InBuffer.

    OutBuffer - Supplies a pointer to the output buffer to be filled in.

    OutBufferSize - Supplies the size, in bytes, of the available space
        pointed to by OutBuffer.

    BytesReturned - Returns the number of bytes of OutBuffer actually
        filled in by the resource. If OutBuffer is too small, BytesReturned
        contains the total number of bytes for the operation to succeed.

Return Value:

    ERROR_SUCCESS - The function completed successfully.

    ERROR_RESOURCE_NOT_FOUND - RESID is not valid.

    ERROR_INVALID_FUNCTION - The requested control code is not supported.
        In some cases, this allows the cluster software to perform the work.

    Win32 error code - The function failed.

--*/

{
    DWORD               status;
    PSMBSMP_RESOURCE  resourceEntry;
    DWORD               required;

    resourceEntry = (PSMBSMP_RESOURCE)ResourceId;

    if ( resourceEntry == NULL ) {
        DBG_PRINT("SmbSmp: ResourceControl request for a nonexistent resource id %u\n",
            ResourceId );
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    if ( resourceEntry->ResId != ResourceId ) {
        (g_LogEvent)(
            resourceEntry->ResourceHandle,
            LOG_ERROR,
            L"ResourceControl sanity check failed! ResourceId = %1!u!.\n",
            ResourceId );
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    switch ( ControlCode ) {

        case CLUSCTL_RESOURCE_UNKNOWN:
            *BytesReturned = 0;
            status = ERROR_SUCCESS;
            break;

        case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES:
            status = ResUtilEnumProperties( SmbSmpResourcePrivateProperties,
                                            OutBuffer,
                                            OutBufferSize,
                                            BytesReturned,
                                            &required );
            if ( status == ERROR_MORE_DATA ) {
                *BytesReturned = required;
            }
            break;

        case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES:
            status = SmbSmpGetPrivateResProperties( resourceEntry,
                                                      OutBuffer,
                                                      OutBufferSize,
                                                      BytesReturned );
            break;

        case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES:
            status = SmbSmpValidatePrivateResProperties( resourceEntry,
                                                           InBuffer,
                                                           InBufferSize,
                                                           NULL );
            break;

        case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES:
            status = SmbSmpSetPrivateResProperties( resourceEntry,
                                                      InBuffer,
                                                      InBufferSize );
            break;

        default:
            status = ERROR_INVALID_FUNCTION;
            break;
    }

    return(status);

} // SmbSmpResourceControl

DWORD
WINAPI
SmbSmpResourceTypeControl(
    IN LPCWSTR ResourceTypeName,
    IN DWORD ControlCode,
    IN PVOID InBuffer,
    IN DWORD InBufferSize,
    OUT PVOID OutBuffer,
    IN DWORD OutBufferSize,
    OUT LPDWORD BytesReturned
    )

/*++

Routine Description:

    ResourceTypeControl routine for SMB Sample resources.

    Perform the control request specified by ControlCode.

Arguments:

    ResourceTypeName - Supplies the name of the resource type.

    ControlCode - Supplies the control code that defines the action
        to be performed.

    InBuffer - Supplies a pointer to a buffer containing input data.

    InBufferSize - Supplies the size, in bytes, of the data pointed
        to by InBuffer.

    OutBuffer - Supplies a pointer to the output buffer to be filled in.

    OutBufferSize - Supplies the size, in bytes, of the available space
        pointed to by OutBuffer.

    BytesReturned - Returns the number of bytes of OutBuffer actually
        filled in by the resource. If OutBuffer is too small, BytesReturned
        contains the total number of bytes for the operation to succeed.

Return Value:

    ERROR_SUCCESS - The function completed successfully.

    ERROR_INVALID_FUNCTION - The requested control code is not supported.
        In some cases, this allows the cluster software to perform the work.

    Win32 error code - The function failed.

--*/

{
    DWORD               status;
    DWORD               required;

    switch ( ControlCode ) {

        case CLUSCTL_RESOURCE_TYPE_UNKNOWN:
            *BytesReturned = 0;
            status = ERROR_SUCCESS;
            break;

        case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES:
            status = ResUtilEnumProperties( SmbSmpResourcePrivateProperties,
                                            OutBuffer,
                                            OutBufferSize,
                                            BytesReturned,
                                            &required );
            if ( status == ERROR_MORE_DATA ) {
                *BytesReturned = required;
            }
            break;

        default:
            status = ERROR_INVALID_FUNCTION;
            break;
    }

    return(status);

} // SmbSmpResourceTypeControl

DWORD
SmbSmpGetPrivateResProperties(
    IN OUT PSMBSMP_RESOURCE ResourceEntry,
    OUT PVOID OutBuffer,
    IN DWORD OutBufferSize,
    OUT LPDWORD BytesReturned
    )

/*++

Routine Description:

    Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control function
    for resources of type SMB Sample.

Arguments:

    ResourceEntry - Supplies the resource entry on which to operate.

    OutBuffer - Returns the output data.

    OutBufferSize - Supplies the size, in bytes, of the data pointed
        to by OutBuffer.

    BytesReturned - The number of bytes returned in OutBuffer.

Return Value:

    ERROR_SUCCESS - The function completed successfully.

    ERROR_INVALID_PARAMETER - The data is formatted incorrectly.

    ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.

    Win32 error code - The function failed.

--*/

{
    DWORD           status;
    DWORD           required;

    status = ResUtilGetAllProperties( ResourceEntry->ParametersKey,
                                      SmbSmpResourcePrivateProperties,
                                      OutBuffer,
                                      OutBufferSize,
                                      BytesReturned,
                                      &required );
    if ( status == ERROR_MORE_DATA ) {
        *BytesReturned = required;
    }

    return(status);

} // SmbSmpGetPrivateResProperties

DWORD
SmbSmpValidatePrivateResProperties(
    IN OUT PSMBSMP_RESOURCE ResourceEntry,
    IN PVOID InBuffer,
    IN DWORD InBufferSize,
    OUT PSMBSMP_PARAMS Params
    )

/*++

Routine Description:

    Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control
    function for resources of type SMB Sample.

Arguments:

    ResourceEntry - Supplies the resource entry on which to operate.

    InBuffer - Supplies a pointer to a buffer containing input data.

    InBufferSize - Supplies the size, in bytes, of the data pointed
        to by InBuffer.

    Params - Supplies the parameter block to fill in.

Return Value:

    ERROR_SUCCESS - The function completed successfully.

    ERROR_INVALID_PARAMETER - The data is formatted incorrectly.

    ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.

    Win32 error code - The function failed.

--*/

{
    DWORD           status = ERROR_SUCCESS;
    SMBSMP_PARAMS   params;
    PSMBSMP_PARAMS  pParams;

    //
    // Check if there is input data.
    //
    if ( (InBuffer == NULL) ||
         (InBufferSize < sizeof(DWORD)) ) {
        return(ERROR_INVALID_DATA);
    }

    //
    // Duplicate the resource parameter block.
    //
    if ( Params == NULL ) {
        pParams = &params;
    } else {
        pParams = Params;
    }
    ZeroMemory( pParams, sizeof(SMBSMP_PARAMS) );
    status = ResUtilDupParameterBlock( (LPBYTE) pParams,
                                       (LPBYTE) &ResourceEntry->Params,
                                       SmbSmpResourcePrivateProperties );
    if ( status != ERROR_SUCCESS ) {
        return(status);
    }

    //
    // Parse and validate the properties.
    //
    status = ResUtilVerifyPropertyTable( SmbSmpResourcePrivateProperties,
                                         NULL,
                                         TRUE, // AllowUnknownProperties
                                         InBuffer,
                                         InBufferSize,
                                         (LPBYTE) pParams );

    if ( status == ERROR_SUCCESS ) {
        //
        // Validate the parameter values.
        //
        // TODO: Code to validate interactions between parameters goes here.
    }

    //
    // Cleanup our parameter block.
    //
    if ( pParams == &params ) {
        ResUtilFreeParameterBlock( (LPBYTE) &params,
                                   (LPBYTE) &ResourceEntry->Params,
                                   SmbSmpResourcePrivateProperties );
    }

    return status;

} // SmbSmpValidatePrivateResProperties

DWORD
SmbSmpSetPrivateResProperties(
    IN OUT PSMBSMP_RESOURCE ResourceEntry,
    IN PVOID InBuffer,
    IN DWORD InBufferSize
    )

/*++

Routine Description:

    Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function
    for resources of type SMB Sample.

Arguments:

    ResourceEntry - Supplies the resource entry on which to operate.

    InBuffer - Supplies a pointer to a buffer containing input data.

    InBufferSize - Supplies the size, in bytes, of the data pointed
        to by InBuffer.

Return Value:

    ERROR_SUCCESS - The function completed successfully.

    ERROR_INVALID_PARAMETER - The data is formatted incorrectly.

    ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.

    Win32 error code - The function failed.

--*/

{
    DWORD           status = ERROR_SUCCESS;
    SMBSMP_PARAMS   params;

    //
    // Parse the properties so they can be validated together.
    // This routine does individual property validation.
    //
    status = SmbSmpValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, &params );
    if ( status != ERROR_SUCCESS ) {
        ResUtilFreeParameterBlock( (LPBYTE) &params,
                                   (LPBYTE) &ResourceEntry->Params,
                                   SmbSmpResourcePrivateProperties );
        return(status);
    }

    //
    // Save the parameter values.
    //

    status = ResUtilSetPropertyParameterBlock( ResourceEntry->ParametersKey,
                                               SmbSmpResourcePrivateProperties,
                                               NULL,
                                               (LPBYTE) &params,
                                               InBuffer,
                                               InBufferSize,
                                               (LPBYTE) &ResourceEntry->Params );

    ResUtilFreeParameterBlock( (LPBYTE) &params,
                               (LPBYTE) &ResourceEntry->Params,
                               SmbSmpResourcePrivateProperties );

    //
    // If the resource is online, return a non-success status.
    //
    // TODO: Modify the code below if your resource can handle
    // changes to properties while it is still online.
    if ( status == ERROR_SUCCESS ) {
        if ( ResourceEntry->State == ClusterResourceOnline ) {
            status = ERROR_RESOURCE_PROPERTIES_STORED;
        } else if ( ResourceEntry->State == ClusterResourceOnlinePending ) {
            status = ERROR_RESOURCE_PROPERTIES_STORED;
        } else {
            status = ERROR_SUCCESS;
        }
    }

    return status;

} // SmbSmpSetPrivateResProperties

//***********************************************************
//
// Define Function Table
//
//***********************************************************

CLRES_V1_FUNCTION_TABLE( g_SmbSmpFunctionTable,     // Name
                         CLRES_VERSION_V1_00,         // Version
                         SmbSmp,                    // Prefix
                         NULL,                        // Arbitrate
                         NULL,                        // Release
                         SmbSmpResourceControl,     // ResControl
                         SmbSmpResourceTypeControl); // ResTypeControl


READEX.TXT

========================================================================
       CLUSTER ADMINISTRATOR EXTENSION : SmbSmp
========================================================================

AppWizard has created this Cluster Administrator Extension DLL for you.
This DLL demonstrates the basics of modifying the interface of
Cluster Administrator and is also a starting point for writing your DLL.

This file contains a summary of what you will find in each of the files that
make up your Cluster Administrator Extension DLL.

SmbSmpEx.h
    This is the main header file for the DLL.  It declares the
    CSmbSmpApp class.

SmbSmpEx.cpp
    This is the main DLL source file.  It contains the class CSmbSmpApp.
    It also contains the OLE entry points required of inproc servers.

SmbSmpEx.rc
    This is a listing of all of the Microsoft Windows resources that the
    program uses.  It includes the icons, bitmaps, and cursors that are stored
    in the RES subdirectory.  This file can be directly edited in Microsoft
    Developer Studio.

res\SmbSmpEx.rc2
    This file contains resources that are not edited by Microsoft
    Developer Studio.  You should place all resources not
    editable by the resource editor in this file.

SmbSmpEx.def
    This file contains information about the DLL that must be
    provided to run with Microsoft Windows.  It defines parameters
    such as the name and description of the DLL.  It also exports
    functions from the DLL.

SmbSmpEx.clw
    This file contains information used by ClassWizard to edit existing
    classes or add new classes.  ClassWizard also uses this file to store
    information needed to create and edit message maps and dialog data
    maps and to create prototype member functions.

ExtObj.h
    This is the header file which defines the classes which implement the
    COM interfaces by the Microsoft Windows NT Cluster Administrator program
    for adding property pages, wizard pages, or context menu items.  It
    defines the CExtObject class.

ExtObj.cpp
    This is the source file which implements the CExtObject class.

ExtObjID.idl
    This the Interface Definition Language source file for defining
    the COM object implemented by the extension DLL.  This is the object
    that will be loaded by the Cluster Administrator program.

/////////////////////////////////////////////////////////////////////////////
Other standard files:

StdAfx.h, StdAfx.cpp
    These files are used to build a precompiled header (PCH) file
    named SmbSmp.pch and a precompiled types file named StdAfx.obj.

Resource.h
    This is the standard header file, which defines new resource IDs.
    Microsoft Developer Studio reads and updates this file.

/////////////////////////////////////////////////////////////////////////////
Property page files:

BasePage.h
    This is the header file which defines a class which provides base
    property page functionality for use by extension property pages.  It
    defines the CBasePropertyPage class.

BasePage.cpp
    This is the source file which implements the CBasePropertyPage class.

BasePage.inl
    This is the source file which implements inline functions for the
    CBasePropertyPage class.

PropList.h
    This is the header file which defines classes for manipulating
    cluster property lists.  It defines the CClusPropList and CObjectProperty
    classes.

PropList.cpp
    This is the source file which implements the CClusPropList and
    CObjectProperty classes for manipulating cluster property lists.

ResProp.h
    This is the header file which defines a property page titled "Parameters"
    to add to property sheets for resources for which your extension DLL is
    written.  It defines the CSmbSmpParamsPage class.

ResProp.cpp
    This is the source file which implements the CSmbSmpParamsPage class.

/////////////////////////////////////////////////////////////////////////////
Other notes:

This application uses the ActiveX Template Library (ATL) V1.1.  ATL V1.1
will need to be installed before your application will build.

To register your extension DLL so that it can be used with the Cluster
Administrator program, use the RegClAdm.exe SDK tool on each machine on
which the Cluster Administrator is to be executed.  RegCladm.exe is
located in the BIN sub-directory of your Visual C++ Installation Directory
(typically C:\MSDEV\BIN):

    RegClAdm [/cclustername] SmbSmpEx.dll

AppWizard uses "TODO:" to indicate parts of the source code you
should add to or customize.

**************************************************************************
  NOTE: You must use the MIDL compiler from NT 4.0,
  version 3.00.44 or greater
**************************************************************************

/////////////////////////////////////////////////////////////////////////////


BASEPAGE.H

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//BasePage.h
//
//Abstract:
//Definition of the CBasePropertyPage class.  This class provides base
//functionality for extension DLL property pages.
//
//Implementation File:
//BasePage.cpp
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#ifndef _BASEPAGE_H_
#define _BASEPAGE_H_

/////////////////////////////////////////////////////////////////////////////
// Include Files
/////////////////////////////////////////////////////////////////////////////

#ifndef _cluadmex_h__
#include <CluAdmEx.h>
#endif

#ifndef _EXTOBJ_H_
#include "ExtObj.h"// for CExtObject
#endif

#ifndef _PROPLIST_H_
#include "PropList.h"// for CClusPropList, CObjectProperty
#endif

/////////////////////////////////////////////////////////////////////////////
// Forward Class Declarations
/////////////////////////////////////////////////////////////////////////////

class CBasePropertyPage;

/////////////////////////////////////////////////////////////////////////////
// External Class Declarations
/////////////////////////////////////////////////////////////////////////////

class CExtObject;
interface IWCWizardCallback;

/////////////////////////////////////////////////////////////////////////////
// CBasePropertyPage dialog
/////////////////////////////////////////////////////////////////////////////

class CBasePropertyPage : public CPropertyPage
{
DECLARE_DYNCREATE(CBasePropertyPage)

// Construction
public:
CBasePropertyPage(void);
CBasePropertyPage(IN UINT nIDTemplate, IN UINT nIDCaption = 0);
virtual ~CBasePropertyPage(void) { }

// Second phase construction.
virtual BOOLBInit(IN OUT CExtObject * peo);
BOOLBCreateParamsKey(void);

protected:
voidCommonConstruct(void);

// Attributes
protected:
CExtObject *m_peo;
HPROPSHEETPAGEm_hpage;

IDDm_iddPropertyPage;
IDDm_iddWizardPage;
IDSm_idsCaption;

HPROPSHEETPAGEHpage(void) const{ return m_hpage; }

IDDIddPropertyPage(void) const{ return m_iddPropertyPage; }
IDDIddWizardPage(void) const{ return m_iddWizardPage; }
IDSIdsCaption(void) const{ return m_idsCaption; }

public:
CExtObject *Peo(void) const{ return m_peo; }
voidSetHpage(IN OUT HPROPSHEETPAGE hpage) { m_hpage = hpage; }
CLUADMEX_OBJECT_TYPECot(void) const;

// Dialog Data
//{{AFX_DATA(CBasePropertyPage)
enum { IDD = 0 };
//}}AFX_DATA
CStaticm_staticIcon;
CStaticm_staticTitle;
CStringm_strTitle;

// Overrides
// ClassWizard generate virtual function overrides
//{{AFX_VIRTUAL(CBasePropertyPage)
public:
virtual BOOL OnSetActive();
virtual BOOL OnApply();
virtual LRESULT OnWizardBack();
virtual LRESULT OnWizardNext();
virtual BOOL OnWizardFinish();
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

virtual DWORDDwParseUnknownProperty(
IN LPCWSTRpwszName,
IN const CLUSPROP_BUFFER_HELPER &rvalue,
IN DWORDcbBuf
){ return ERROR_SUCCESS; }
virtual BOOLBApplyChanges(void);
virtual voidBuildPropList(IN OUT CClusPropList & rcpl);

virtual const CObjectProperty *Pprops(void) const{ return NULL; }
virtual DWORDCprops(void) const{ return 0; }

// Implementation
protected:
BOOLm_bBackPressed;

BOOLBBackPressed(void) const{ return m_bBackPressed; }
IWCWizardCallback *PiWizardCallback(void) const;
BOOLBWizard(void) const;
HCLUSTERHcluster(void) const;
voidEnableNext(IN BOOL bEnable = TRUE);

DWORDDwParseProperties(IN const CClusPropList & rcpl);
BOOLBSetPrivateProps(IN BOOL bValidateOnly = FALSE);

// Generated message map functions
//{{AFX_MSG(CBasePropertyPage)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnDestroy();
virtual BOOL OnInitDialog();
//}}AFX_MSG
afx_msg void OnChangeCtrl();
DECLARE_MESSAGE_MAP()

};  //*** class CBasePropertyPage

/////////////////////////////////////////////////////////////////////////////
// CPageList
/////////////////////////////////////////////////////////////////////////////

typedef CList<CBasePropertyPage *, CBasePropertyPage *> CPageList;

/////////////////////////////////////////////////////////////////////////////

#endif // _BASEPAGE_H_


DDXDDV.CPP

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//DDxDDv.cpp
//
//Abstract:
//Implementation of custom dialog data exchange/dialog data validation
//routines.
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//The IDS_REQUIRED_FIELD_EMPTY string resource must be defined in
//the resource file.
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "DDxDDv.h"

#include "resource.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Static Function Prototypes
/////////////////////////////////////////////////////////////////////////////
static void CleanupLabel(LPTSTR psz);

/////////////////////////////////////////////////////////////////////////////
//++
//
//DDX_Number
//
//Routine Description:
//Do data exchange between the dialog and the class.
//
//Arguments:
//pDX[IN OUT] Data exchange object
//nIDC[IN] Control ID.
//dwValue[IN OUT] Value to set or get.
//dwMin[IN] Minimum value.
//dwMax[IN] Maximum value.
//bSigned[IN] TRUE = value is signed, FALSE = value is unsigned
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void AFXAPI DDX_Number(
IN OUT CDataExchange *pDX,
IN intnIDC,
IN OUT DWORD &rdwValue,
IN DWORDdwMin,
IN DWORDdwMax,
IN BOOLbSigned
)
{
HWNDhwndCtrl;
DWORDdwValue;

ASSERT(pDX != NULL);
#ifdef _DEBUG
if (bSigned)
{
ASSERT((LONG) dwMin < (LONG) dwMax);
}
else
{
ASSERT(dwMin < dwMax);
}
#endif // _DEBUG

AFX_MANAGE_STATE(AfxGetStaticModuleState());

// Get the control window handle.
hwndCtrl = pDX->PrepareEditCtrl(nIDC);

if (pDX->m_bSaveAndValidate)
{
BOOLbTranslated;

// Get the number from the control.
dwValue = GetDlgItemInt(pDX->m_pDlgWnd->m_hWnd, nIDC, &bTranslated, bSigned);

// If the retrival failed, it is a signed number, and the minimum
// value is the smallest negative value possible, check the string itself.
if (!bTranslated && bSigned && (dwMin == 0x80000000))
{
UINTcch;
TCHARszNumber[20];

// See if it is the smallest negative number.
cch = GetDlgItemText(pDX->m_pDlgWnd->m_hWnd, nIDC, szNumber, sizeof(szNumber) / sizeof(TCHAR));
if ((cch != 0) && (lstrcmp(szNumber, _T("-2147483648")) == 0))
{
dwValue = 0x80000000;
bTranslated = TRUE;
}  // if:  text retrieved successfully and is highest negative number
}  // if:  error translating number and getting signed number

// If the retrieval failed or the specified number is
// out of range, display an error.
if (   !bTranslated
|| (bSigned && (((LONG) dwValue < (LONG) dwMin) || ((LONG) dwValue > (LONG) dwMax)))
|| (!bSigned && ((dwValue < dwMin) || (dwValue > dwMax)))
)
{
TCHAR szMin[32];
TCHAR szMax[32];
CString strPrompt;

if (bSigned)
{
wsprintf(szMin, _T("%d%"), dwMin);
wsprintf(szMax, _T("%d%"), dwMax);
}  // if:  signed number
else
{
wsprintf(szMin, _T("%u%"), dwMin);
wsprintf(szMax, _T("%u%"), dwMax);
}  // else:  unsigned number
AfxFormatString2(strPrompt, AFX_IDP_PARSE_INT_RANGE, szMin, szMax);
AfxMessageBox(strPrompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_INT_RANGE);
strPrompt.Empty(); // exception prep
pDX->Fail();
}  // if:  invalid string
else
rdwValue = dwValue;
}  // if:  saving data
else
{
CStringstrMinValue;
CStringstrMaxValue;
UINTcchMax;

// Set the maximum number of characters that can be entered.
if (bSigned)
{
strMinValue.Format(_T("%d"), dwMin);
strMaxValue.Format(_T("%d"), dwMax);
}  // if:  signed value
else
{
strMinValue.Format(_T("%u"), dwMin);
strMaxValue.Format(_T("%u"), dwMax);
}  // else:  unsigned value
cchMax = max(strMinValue.GetLength(), strMaxValue.GetLength());
SendMessage(hwndCtrl, EM_LIMITTEXT, cchMax, 0);

// Set the value into the control.
if (bSigned)
{
LONG lValue = (LONG) rdwValue;
DDX_Text(pDX, nIDC, lValue);
}  // if:  signed value
else
DDX_Text(pDX, nIDC, rdwValue);
}  // else:  setting data onto the dialog

}  //*** DDX_Number()

/////////////////////////////////////////////////////////////////////////////
//++
//
//DDV_RequiredText
//
//Routine Description:
//Validate that the dialog string is present.
//
//Arguments:
//pDX[IN OUT] Data exchange object
//nIDC[IN] Control ID.
//nIDCLabel[IN] Label control ID.
//rstrValue[IN] Value to set or get.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void AFXAPI DDV_RequiredText(
IN OUT CDataExchange *pDX,
IN intnIDC,
IN intnIDCLabel,
IN const CString &rstrValue
)
{
ASSERT(pDX != NULL);

AFX_MANAGE_STATE(AfxGetStaticModuleState());

if (pDX->m_bSaveAndValidate)
{
if (rstrValue.GetLength() == 0)
{
HWNDhwndLabel;
TCHARszLabel[1024];
CStringstrPrompt;

// Get the label window handle
hwndLabel = pDX->PrepareEditCtrl(nIDCLabel);

// Get the text of the label.
GetWindowText(hwndLabel, szLabel, sizeof(szLabel) / sizeof(TCHAR));

// Remove ampersands (&) and colons (:).
CleanupLabel(szLabel);

// Format and display a message.
strPrompt.FormatMessage(IDS_REQUIRED_FIELD_EMPTY, szLabel);
AfxMessageBox(strPrompt, MB_ICONEXCLAMATION);

// Do this so that the control receives focus.
(void) pDX->PrepareEditCtrl(nIDC);

// Fail the call.
strPrompt.Empty();// exception prep
pDX->Fail();
}  // if:  field not specified
}  // if:  saving data

}  //*** DDV_RequiredText()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CleanupLabel
//
//Routine Description:
//Prepare a label read from a dialog to be used as a string in a
//message by removing ampersands (&) and colons (:).
//
//Arguments:
//pszLabel[IN OUT] Label to be cleaned up.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
static void CleanupLabel(LPTSTR pszLabel)
{
LPTSTRpIn, pOut;
LANGIDlangid;
WORDprimarylangid;
BOOLbFELanguage;

// Get the language ID.
langid = GetUserDefaultLangID();
primarylangid = (WORD) PRIMARYLANGID(langid);
bFELanguage = ((primarylangid == LANG_JAPANESE)
|| (primarylangid == LANG_CHINESE)
|| (primarylangid == LANG_KOREAN));

//
// copy the name sans '&' and ':' chars
//

pIn = pOut = pszLabel;
do
{
//
// strip FE accelerators with parentheses. e.g. "foo(&F)" -> "foo"
//
if (   bFELanguage
&& (pIn[0] == _T('('))
&& (pIn[1] == _T('&'))
&& (pIn[2] != _T('\0'))
&& (pIn[3] == _T(')')))
{
pIn += 3;
}
else if ((*pIn != _T('&')) && (*pIn != _T(':')))
*pOut++ = *pIn;
} while (*pIn++ != _T('\0')) ;

}  //*** CleanupLabel()


DDXDDV.H

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//DDxDDv.h
//
//Abstract:
//Definition of custom dialog data exchange/dialog data validation
//routines.
//
//Implementation File:
//DDxDDv.cpp
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#ifndef _DDXDDV_H_
#define _DDXDDV_H_

/////////////////////////////////////////////////////////////////////////////
// Include Files
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// Global Function Prototypes
/////////////////////////////////////////////////////////////////////////////

void AFXAPI DDX_Number(
IN OUT CDataExchange *pDX,
IN intnIDC,
IN OUT DWORD &rdwValue,
IN DWORDdwMin,
IN DWORDdwMax,
IN BOOLbSigned = FALSE
);
void AFXAPI DDV_RequiredText(
IN OUT CDataExchange *pDX,
IN intnIDC,
IN intnIDCLabel,
IN const CString &rstrValue
);

inline void AFXAPI DDX_Number(
IN OUT CDataExchange *pDX,
IN intnIDC,
IN OUT LONG &rnValue,
IN LONGnMin,
IN LONGnMax,
IN BOOLbSigned
)
{
DDX_Number(pDX, nIDC, (DWORD &) rnValue, (DWORD) nMin, (DWORD) nMax, bSigned);
}

/////////////////////////////////////////////////////////////////////////////

#endif // _DDXDDV_H_


EXTOBJ.CPP

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//ExtObj.cpp
//
//Abstract:
//Implementation of the CExtObject class, which implements the
//extension interfaces required by a Microsoft Windows NT Cluster
//Administrator Extension DLL.
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SmbSmpEx.h"
#include "ExtObj.h"
#include "ResProp.h"

/////////////////////////////////////////////////////////////////////////////
// Global Variables
/////////////////////////////////////////////////////////////////////////////

const WCHAR g_wszResourceTypeNames[] =
L"SMB Sample\0"
L"\0"
;
const DWORD g_cchResourceTypeNames= sizeof(g_wszResourceTypeNames) / sizeof(WCHAR);

static CRuntimeClass * g_rgprtcResPSPages[]= {
RUNTIME_CLASS(CSmbSmpParamsPage),
NULL
};
static CRuntimeClass ** g_rgpprtcResPSPages[]= {
g_rgprtcResPSPages,
};
static CRuntimeClass ** g_rgpprtcResWizPages[]= {
g_rgprtcResPSPages,
};

/////////////////////////////////////////////////////////////////////////////
// CExtObject
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
//++
//
//CExtObject::CExtObject
//
//Routine Description:
//Default constructor.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CExtObject::CExtObject(void)
{
m_piData = NULL;
m_piWizardCallback = NULL;
m_bWizard = FALSE;
m_istrResTypeName = 0;

m_lcid = NULL;
m_hfont = NULL;
m_hicon = NULL;
m_hcluster = NULL;
m_cobj = 0;
m_podObjData = NULL;

}  //*** CExtObject::CExtObject()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CExtObject::~CExtObject
//
//Routine Description:
//Destructor.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CExtObject::~CExtObject(void)
{
// Release the data interface.
if (PiData() != NULL)
{
PiData()->Release();
m_piData = NULL;
}  // if:  we have a data interface pointer

// Release the wizard callback interface.
if (PiWizardCallback() != NULL)
{
PiWizardCallback()->Release();
m_piWizardCallback = NULL;
}  // if:  we have a wizard callback interface pointer

// Delete the pages.
{
POSITIONpos;

pos = Lpg().GetHeadPosition();
while (pos != NULL)
delete Lpg().GetNext(pos);
}  // Delete the pages

delete m_podObjData;

}  //*** CExtObject::~CExtObject()

/////////////////////////////////////////////////////////////////////////////
// ISupportErrorInfo Implementation

/////////////////////////////////////////////////////////////////////////////
//++
//
//CExtObject::InterfaceSupportsErrorInfo (ISupportErrorInfo)
//
//Routine Description:
//Indicates whether an interface suportes the IErrorInfo interface.
//This interface is provided by ATL.
//
//Arguments:
//riidInterface ID.
//
//Return Value:
//S_OKInterface supports IErrorInfo.
//S_FALSEInterface does not support IErrorInfo.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CExtObject::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID * rgiid[] =
{
&IID_IWEExtendPropertySheet,
&IID_IWEExtendWizard,
};
intiiid;

for (iiid = 0 ; iiid < sizeof(rgiid) / sizeof(rgiid[0]) ; iiid++)
{
if (InlineIsEqualGUID(*rgiid[iiid], riid))
return S_OK;
}
return S_FALSE;

}  //*** CExtObject::InterfaceSupportsErrorInfo()

/////////////////////////////////////////////////////////////////////////////
// IWEExtendPropertySheet Implementation

/////////////////////////////////////////////////////////////////////////////
//++
//
//CExtObject::CreatePropertySheetPages (IWEExtendPropertySheet)
//
//Routine Description:
//Create property sheet pages and add them to the sheet.
//
//Arguments:
//piDataIUnkown pointer from which to obtain interfaces
//for obtaining data describing the object for
//which the sheet is being displayed.
//piCallbackPointer to an IWCPropertySheetCallback interface
//for adding pages to the sheet.
//
//Return Value:
//NOERRORPages added successfully.
//E_INVALIDARGInvalid arguments to the function.
//E_OUTOFMEMORYError allocating memory.
//E_FAILError creating a page.
//E_NOTIMPLNot implemented for this type of data.
//Any error codes from IDataObject::GetData() (through HrSaveData()).
//
//--
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CExtObject::CreatePropertySheetPages(
IN IUnknown *piData,
IN IWCPropertySheetCallback *piCallback
)
{
HRESULThr= NOERROR;
HPROPSHEETPAGEhpage= NULL;
CExceptionexc(FALSE /*bAutoDelete*/);
CRuntimeClass **pprtc= NULL;
intirtc;
CBasePropertyPage *ppage;

AFX_MANAGE_STATE(AfxGetStaticModuleState());

// Validate the parameters.
if ((piData == NULL) || (piCallback == NULL))
return E_INVALIDARG;

try
{
// Get info about displaying UI.
hr = HrGetUIInfo(piData);
if (hr != NOERROR)
throw &exc;

// Save the data.
hr = HrSaveData(piData);
if (hr != NOERROR)
throw &exc;

// Delete any previous pages.
{
POSITIONpos;

pos = Lpg().GetHeadPosition();
while (pos != NULL)
delete Lpg().GetNext(pos);
Lpg().RemoveAll();
}  // Delete any previous pages

// Create property pages.
ASSERT(PodObjData() != NULL);
switch (PodObjData()->m_cot)
{
case CLUADMEX_OT_RESOURCE:
pprtc = g_rgpprtcResPSPages[IstrResTypeName()];
break;
default:
hr = E_NOTIMPL;
throw &exc;
break;
}  // switch:  object type

// Create each page.
for (irtc = 0 ; pprtc[irtc] != NULL ; irtc++)
{
// Create the page.
ppage = (CBasePropertyPage *) pprtc[irtc]->CreateObject();
ASSERT(ppage->IsKindOf(pprtc[irtc]));

// Add it to the list.
Lpg().AddTail(ppage);

// Initialize the property page.
if (!ppage->BInit(this))
throw &exc;

// Create the page.
hpage = ::CreatePropertySheetPage(&ppage->m_psp);
if (hpage == NULL)
throw &exc;

// Save the hpage in the page itself.
ppage->SetHpage(hpage);

// Add it to the property sheet.
hr = piCallback->AddPropertySheetPage((LONG *) hpage);
if (hr != NOERROR)
throw &exc;
}  // for:  each page in the list

}  // try
catch (CMemoryException * pme)
{
TRACE(_T("CExtObject::CreatePropetySheetPages() - Failed to add property page\n"));
pme->Delete();
hr = E_OUTOFMEMORY;
}  // catch:  anything
catch (CException * pe)
{
TRACE(_T("CExtObject::CreatePropetySheetPages() - Failed to add property page\n"));
pe->Delete();
if (hr == NOERROR)
hr = E_FAIL;
}  // catch:  anything

if (hr != NOERROR)
{
if (hpage != NULL)
::DestroyPropertySheetPage(hpage);
piData->Release();
m_piData = NULL;
}  // if:  error occurred

piCallback->Release();
return hr;

}  //*** CExtObject::CreatePropertySheetPages()

/////////////////////////////////////////////////////////////////////////////
// IWEExtendWizard Implementation

/////////////////////////////////////////////////////////////////////////////
//++
//
//CExtObject::CreateWizardPages (IWEExtendWizard)
//
//Routine Description:
//Create property sheet pages and add them to the wizard.
//
//Arguments:
//piDataIUnkown pointer from which to obtain interfaces
//for obtaining data describing the object for
//which the wizard is being displayed.
//piCallbackPointer to an IWCPropertySheetCallback interface
//for adding pages to the sheet.
//
//Return Value:
//NOERRORPages added successfully.
//E_INVALIDARGInvalid arguments to the function.
//E_OUTOFMEMORYError allocating memory.
//E_FAILError creating a page.
//E_NOTIMPLNot implemented for this type of data.
//Any error codes from IDataObject::GetData() (through HrSaveData()).
//
//--
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CExtObject::CreateWizardPages(
IN IUnknown *piData,
IN IWCWizardCallback *piCallback
)
{
HRESULThr= NOERROR;
HPROPSHEETPAGEhpage= NULL;
CExceptionexc(FALSE /*bAutoDelete*/);
CRuntimeClass **pprtc= NULL;
intirtc;
CBasePropertyPage *ppage;

AFX_MANAGE_STATE(AfxGetStaticModuleState());

// Validate the parameters.
if ((piData == NULL) || (piCallback == NULL))
return E_INVALIDARG;

try
{
// Get info about displaying UI.
hr = HrGetUIInfo(piData);
if (hr != NOERROR)
throw &exc;

// Save the data.
hr = HrSaveData(piData);
if (hr != NOERROR)
throw &exc;

// Delete any previous pages.
{
POSITIONpos;

pos = Lpg().GetHeadPosition();
while (pos != NULL)
delete Lpg().GetNext(pos);
Lpg().RemoveAll();
}  // Delete any previous pages

m_piWizardCallback = piCallback;
m_bWizard = TRUE;

// Create property pages.
ASSERT(PodObjData() != NULL);
switch (PodObjData()->m_cot)
{
case CLUADMEX_OT_RESOURCE:
pprtc = g_rgpprtcResWizPages[IstrResTypeName()];
break;
default:
hr = E_NOTIMPL;
throw &exc;
break;
}  // switch:  object type

// Create each page.
for (irtc = 0 ; pprtc[irtc] != NULL ; irtc++)
{
// Create the page.
ppage = (CBasePropertyPage *) pprtc[irtc]->CreateObject();
ASSERT(ppage->IsKindOf(pprtc[irtc]));

// Add it to the list.
Lpg().AddTail(ppage);

// Initialize the property page.
if (!ppage->BInit(this))
throw &exc;

// Create the page.
hpage = ::CreatePropertySheetPage(&ppage->m_psp);
if (hpage == NULL)
throw &exc;

// Save the hpage in the page itself.
ppage->SetHpage(hpage);

// Add it to the property sheet.
hr = piCallback->AddWizardPage((LONG *) hpage);
if (hr != NOERROR)
throw &exc;
}  // for:  each page in the list

}  // try
catch (CMemoryException * pme)
{
TRACE(_T("CExtObject::CreateWizardPages() - Failed to add wizard page\n"));
pme->Delete();
hr = E_OUTOFMEMORY;
}  // catch:  anything
catch (CException * pe)
{
TRACE(_T("CExtObject::CreateWizardPages() - Failed to add wizard page\n"));
pe->Delete();
if (hr == NOERROR)
hr = E_FAIL;
}  // catch:  anything

if (hr != NOERROR)
{
if (hpage != NULL)
::DestroyPropertySheetPage(hpage);
piCallback->Release();
piData->Release();
m_piData = NULL;
}  // if:  error occurred

return hr;

}  //*** CExtObject::CreateWizardPages()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CExtObject::HrGetUIInfo
//
//Routine Description:
//Get info about displaying UI.
//
//Arguments:
//piDataIUnkown pointer from which to obtain interfaces
//for obtaining data describing the object.
//
//Return Value:
//NOERRORData saved successfully.
//E_NOTIMPLNot implemented for this type of data.
//Any error codes from IUnknown::QueryInterface(), HrGetObjectName(),
//or HrGetResourceName().
//
//--
/////////////////////////////////////////////////////////////////////////////
HRESULT CExtObject::HrGetUIInfo(IUnknown * piData)
{
HRESULThr= NOERROR;

ASSERT(piData != NULL);

// Save info about all types of objects.
{
IGetClusterUIInfo *pi;

hr = piData->QueryInterface(IID_IGetClusterUIInfo, (LPVOID *) &pi);
if (hr != NOERROR)
return hr;

m_lcid = pi->GetLocale();
m_hfont = pi->GetFont();
m_hicon = pi->GetIcon();

pi->Release();
}  // Save info about all types of objects

return hr;

}  //*** CExtObject::HrGetUIInfo()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CExtObject::HrSaveData
//
//Routine Description:
//Save data from the object so that it can be used for the life
//of the object.
//
//Arguments:
//piDataIUnkown pointer from which to obtain interfaces
//for obtaining data describing the object.
//
//Return Value:
//NOERRORData saved successfully.
//E_NOTIMPLNot implemented for this type of data.
//Any error codes from IUnknown::QueryInterface(), HrGetObjectName(),
//or HrGetResourceName().
//
//--
/////////////////////////////////////////////////////////////////////////////
HRESULT CExtObject::HrSaveData(IUnknown * piData)
{
HRESULThr= NOERROR;

ASSERT(piData != NULL);

if (piData != m_piData)
{
if (m_piData != NULL)
m_piData->Release();
m_piData = piData;
}  // if:  different data interface pointer

// Save info about all types of objects.
{
IGetClusterDataInfo *pi;

hr = piData->QueryInterface(IID_IGetClusterDataInfo, (LPVOID *) &pi);
if (hr != NOERROR)
return hr;

m_hcluster = pi->GetClusterHandle();
m_cobj = pi->GetObjectCount();
if (Cobj() != 1)// Only have support for one selected object.
hr = E_NOTIMPL;

pi->Release();
if (hr != NOERROR)
return hr;
}  // Save info about all types of objects

// Save info about this object.
hr = HrGetObjectInfo();
if (hr != NOERROR)
return hr;

return hr;

}  //*** CExtObject::HrSaveData()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CExtObject::HrGetObjectInfo
//
//Routine Description:
//Get information about the object.
//
//Arguments:
//None.
//
//Return Value:
//NOERRORData saved successfully.
//E_OUTOFMEMORYError allocating memory.
//E_NOTIMPLNot implemented for this type of data.
//Any error codes from IUnknown::QueryInterface(), HrGetObjectName(),
//or HrGetResourceTypeName().
//
//--
/////////////////////////////////////////////////////////////////////////////
HRESULT CExtObject::HrGetObjectInfo(void)
{
HRESULThr= NOERROR;
IGetClusterObjectInfo *piGcoi;
CLUADMEX_OBJECT_TYPEcot;
CExceptionexc(FALSE /*bAutoDelete*/);
const CString *pstrResTypeName;

ASSERT(PiData() != NULL);

// Get object info.
{
// Get an IGetClusterObjectInfo interface pointer.
hr = PiData()->QueryInterface(IID_IGetClusterObjectInfo, (LPVOID *) &piGcoi);
if (hr != NOERROR)
return hr;

// Read the object data.
try
{
// Delete the previous object data.
delete m_podObjData;
m_podObjData = NULL;

// Get the type of the object.
cot = piGcoi->GetObjectType(0);
switch (cot)
{
case CLUADMEX_OT_RESOURCE:
{
IGetClusterResourceInfo *pi;

m_podObjData = new CResData;

// Get an IGetClusterResourceInfo interface pointer.
hr = PiData()->QueryInterface(IID_IGetClusterResourceInfo, (LPVOID *) &pi);
if (hr != NOERROR)
throw &exc;

PrdResDataRW()->m_hresource = pi->GetResourceHandle(0);
ASSERT(PrdResDataRW()->m_hresource != NULL);
if (PrdResDataRW()->m_hresource == NULL)
hr = E_INVALIDARG;
else
hr = HrGetResourceTypeName(pi);
pi->Release();
if (hr != NOERROR)
throw &exc;

pstrResTypeName = &PrdResDataRW()->m_strResTypeName;
}  // if:  object is a node
break;
default:
hr = E_NOTIMPL;
throw &exc;
}  // switch:  object type

PodObjDataRW()->m_cot = cot;
hr = HrGetObjectName(piGcoi);
}  // try
catch (CException * pe)
{
pe->Delete();
}  // catch:  CException

piGcoi->Release();
if (hr != NOERROR)
return hr;
}  // Get object info

// If this is a resource or resource type, see if we know about this type.
if (((cot == CLUADMEX_OT_RESOURCE)
|| (cot == CLUADMEX_OT_RESOURCETYPE))
&& (hr == NOERROR))
{
LPCWSTRpwszResTypeName;

// Find the resource type name in our list.
// Save the index for use in other arrays.
for (m_istrResTypeName = 0, pwszResTypeName = g_wszResourceTypeNames
; *pwszResTypeName != L'\0'
; m_istrResTypeName++, pwszResTypeName += lstrlenW(pwszResTypeName) + 1
)
{
if (pstrResTypeName->CompareNoCase(pwszResTypeName) == 0)
break;
}  // for:  each resource type in the list
if (*pwszResTypeName == L'\0')
hr = E_NOTIMPL;
}  // See if we know about this resource type

return hr;

}  //*** CExtObject::HrGetObjectInfo()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CExtObject::HrGetObjectName
//
//Routine Description:
//Get the name of the object.
//
//Arguments:
//piDataIGetClusterObjectInfo interface pointer for getting
//the object name.
//
//Return Value:
//NOERRORData saved successfully.
//E_OUTOFMEMORYError allocating memory.
//E_NOTIMPLNot implemented for this type of data.
//Any error codes from IGetClusterObjectInfo::GetObjectInfo().
//
//--
/////////////////////////////////////////////////////////////////////////////
HRESULT CExtObject::HrGetObjectName(
IN OUT IGetClusterObjectInfo *pi
)
{
HRESULThr= NOERROR;
WCHAR *pwszName= NULL;
LONGcchName;

ASSERT(pi != NULL);

hr = pi->GetObjectName(0, NULL, &cchName);
if (hr != NOERROR)
return hr;

try
{
pwszName = new WCHAR[cchName];
hr = pi->GetObjectName(0, pwszName, &cchName);
if (hr != NOERROR)
{
delete [] pwszName;
pwszName = NULL;
}  // if:  error getting object name

PodObjDataRW()->m_strName = pwszName;
}  // try
catch (CMemoryException * pme)
{
pme->Delete();
hr = E_OUTOFMEMORY;
}  // catch:  CMemoryException

delete [] pwszName;
return hr;

}  //*** CExtObject::HrGetObjectName()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CExtObject::HrGetResourceTypeName
//
//Routine Description:
//Get the name of the resource's type.
//
//Arguments:
//piDataIGetClusterResourceInfo interface pointer for getting
//the resource type name.
//
//Return Value:
//NOERRORData saved successfully.
//E_OUTOFMEMORYError allocating memory.
//E_NOTIMPLNot implemented for this type of data.
//Any error codes from IGetClusterResourceInfo::GetResourceTypeName().
//
//--
/////////////////////////////////////////////////////////////////////////////
HRESULT CExtObject::HrGetResourceTypeName(
IN OUT IGetClusterResourceInfo *pi
)
{
HRESULThr= NOERROR;
WCHAR *pwszName= NULL;
LONGcchName;

ASSERT(pi != NULL);

hr = pi->GetResourceTypeName(0, NULL, &cchName);
if (hr != NOERROR)
return hr;

try
{
pwszName = new WCHAR[cchName];
hr = pi->GetResourceTypeName(0, pwszName, &cchName);
if (hr != NOERROR)
{
delete [] pwszName;
pwszName = NULL;
}  // if:  error getting resource type name

PrdResDataRW()->m_strResTypeName = pwszName;
}  // try
catch (CMemoryException * pme)
{
pme->Delete();
hr = E_OUTOFMEMORY;
}  // catch:  CMemoryException

delete [] pwszName;
return hr;

}  //*** CExtObject::HrGetResourceTypeName()


EXTOBJ.H

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//ExtObj.h
//
//Abstract:
//Definition of the CExtObject class, which implements the
//extension interfaces required by a Microsoft Windows NT Cluster
//Administrator Extension DLL.
//
//Implementation File:
//ExtObj.cpp
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#ifndef _EXTOBJ_H_
#define _EXTOBJ_H_

/////////////////////////////////////////////////////////////////////////////
// Include Files
/////////////////////////////////////////////////////////////////////////////

#ifndef __cluadmex_h__
#include <CluAdmEx.h>// for CLUADMEX_OBJECT_TYPE and interfaces
#endif

#ifndef __extobj_idl_h__
#include "ExtObjID.h"// for CLSID_CoSmbSmpEx
#endif

/////////////////////////////////////////////////////////////////////////////
// Forward Class Declarations
/////////////////////////////////////////////////////////////////////////////

class CExtObject;
class CObjData;
class CResData;

/////////////////////////////////////////////////////////////////////////////
// External Class Declarations
/////////////////////////////////////////////////////////////////////////////

class CBasePropertyPage;

/////////////////////////////////////////////////////////////////////////////
// CPageList
/////////////////////////////////////////////////////////////////////////////

typedef CList<CBasePropertyPage *, CBasePropertyPage *> CPageList;

/////////////////////////////////////////////////////////////////////////////
// class CObjData
/////////////////////////////////////////////////////////////////////////////

class CObjData
{
public:
CStringm_strName;
CLUADMEX_OBJECT_TYPEm_cot;

virtual ~CObjData(void) { }

};  //*** class CObjData

/////////////////////////////////////////////////////////////////////////////
// class CNodeData
/////////////////////////////////////////////////////////////////////////////

class CNodeData : public CObjData
{
public:
HNODEm_hnode;

};  //*** class CNodeData

/////////////////////////////////////////////////////////////////////////////
// class CGroupData
/////////////////////////////////////////////////////////////////////////////

class CGroupData : public CObjData
{
public:
HGROUPm_hgroup;

};  //*** class CGroupData

/////////////////////////////////////////////////////////////////////////////
// class CResData
/////////////////////////////////////////////////////////////////////////////

class CResData : public CObjData
{
public:
HRESOURCEm_hresource;
CStringm_strResTypeName;

};  //*** class CResData

/////////////////////////////////////////////////////////////////////////////
// class CNetworkData
/////////////////////////////////////////////////////////////////////////////

class CNetworkData : public CObjData
{
public:
HNETWORKm_hnetwork;

};  //*** class CNetworkData

/////////////////////////////////////////////////////////////////////////////
// class CNetInterfaceData
/////////////////////////////////////////////////////////////////////////////

class CNetInterfaceData : public CObjData
{
public:
HNETINTERFACEm_hnetinterface;

};  //*** class CNetInterfaceData

/////////////////////////////////////////////////////////////////////////////
// class CExtObject
/////////////////////////////////////////////////////////////////////////////

//REVIEW -- using pointers to ID's is necessary because some compilers don't like
//references as template arguments.

class CExtObject :
public IWEExtendPropertySheet,
public IWEExtendWizard,
public ISupportErrorInfo,
public CComObjectRoot,
public CComCoClass<CExtObject, &CLSID_CoSmbSmpEx>
{
public:
CExtObject(void);
BEGIN_COM_MAP(CExtObject)
COM_INTERFACE_ENTRY(IWEExtendPropertySheet)
COM_INTERFACE_ENTRY(IWEExtendWizard)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()
//DECLARE_NOT_AGGREGATABLE(CExtObject)
// Remove the comment from the line above if you don't want your object to
// support aggregation.  The default is to support it

DECLARE_REGISTRY(CExtObject, _T("CLUADMEX.SmbSmpEx"), _T("CLUADMEX.SmbSmpEx"), IDS_CLUADMEX_COMOBJ_DESC, THREADFLAGS_APARTMENT)

// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IWEExtendPropertySheet
public:
STDMETHOD(CreatePropertySheetPages)(
IN IUnknown *piData,
IN IWCPropertySheetCallback *piCallback
);

// IWEExtendWizard
public:
STDMETHOD(CreateWizardPages)(
IN IUnknown *piData,
IN IWCWizardCallback *piCallback
);

// Attributes
protected:
IUnknown *m_piData;
IWCWizardCallback *m_piWizardCallback;
BOOLm_bWizard;
DWORDm_istrResTypeName;

// IGetClusterUIInfo data
LCIDm_lcid;
HFONTm_hfont;
HICONm_hicon;

// IGetClusterDataInfo data
HCLUSTERm_hcluster;
LONGm_cobj;

CObjData *m_podObjData;

CObjData *PodObjDataRW(void) const{ return m_podObjData; }
CResData *PrdResDataRW(void) const{ return (CResData *) m_podObjData; }

public:
IUnknown *PiData(void) const{ return m_piData; }
IWCWizardCallback *PiWizardCallback(void) const{ return m_piWizardCallback; }
BOOLBWizard(void) const{ return m_bWizard; }
DWORDIstrResTypeName(void) const{ return m_istrResTypeName; }

// IGetClusterUIInfo data
LCIDLcid(void) const{ return m_lcid; }
HFONTHfont(void) const{ return m_hfont; }
HICONHicon(void) const{ return m_hicon; }

// IGetClusterDataInfo data
HCLUSTERHcluster(void) const{ return m_hcluster; }
LONGCobj(void) const{ return m_cobj; }

const CObjData *PodObjData(void) const{ return m_podObjData; }
const CNodeData *PndNodeData(void) const{ ASSERT(Cot() == CLUADMEX_OT_NODE); return (CNodeData *) m_podObjData; }
const CGroupData *PgdGroupData(void) const{ ASSERT(Cot() == CLUADMEX_OT_GROUP); return (CGroupData *) m_podObjData; }
const CResData *PrdResData(void) const{ ASSERT(Cot() == CLUADMEX_OT_RESOURCE); return (CResData *) m_podObjData; }
const CNetworkData *PndNetworkData(void) const{ ASSERT(Cot() == CLUADMEX_OT_NETWORK); return (CNetworkData *) m_podObjData; }
const CNetInterfaceData *PndNetInterfaceData(void) const{ ASSERT(Cot() == CLUADMEX_OT_NETINTERFACE); return (CNetInterfaceData *) m_podObjData; }

CLUADMEX_OBJECT_TYPECot(void) const{ ASSERT(PodObjData() != NULL); return PodObjData()->m_cot; }

HRESULTHrGetUIInfo(IUnknown * piData);
HRESULTHrSaveData(IUnknown * piData);
HRESULTHrGetObjectInfo(void);
HRESULTHrGetObjectName(IN OUT IGetClusterObjectInfo * pi);
HRESULTHrGetResourceTypeName(IN OUT IGetClusterResourceInfo * pi);

// Implementation
protected:
virtual ~CExtObject(void);

CPageListm_lpg;
CPageList &Lpg(void){ return m_lpg; }

};  //*** class CExtObject

/////////////////////////////////////////////////////////////////////////////

#endif // _EXTOBJ_H_


EXTOBJID.IDL

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//ExtObjID.idl
//
//Abstract:
//Definition of COM interfaces for the Cluster Administrator
//SmbSmpEx Extension DLL.
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

// This file will be processed by the MIDL tool to
// produce the type library (ExtObj.tlb) and marshalling code.

[
uuid(6F7AB790-E86B-11D0-85CE-00A0C9055DA4),
version(1.0),
helpstring("SmbSmpEx 1.0 Type Library")
]
library CLUADMEXLib
{
importlib("stdole2.tlb");
import "cluadmex.idl";

[
uuid(6F7AB791-E86B-11D0-85CE-00A0C9055DA4),
helpstring("CoSmbSmpEx Class")
]
coclass CoSmbSmpEx
{
interface IWEExtendPropertySheet;
interface IWEExtendWizard;
};

};


PROPLIST.CPP

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//PropList.cpp
//
//Abstract:
//Implementation of the CClusPropList class.
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "PropList.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Constant Definitions
/////////////////////////////////////////////////////////////////////////////

#define BUFFER_GROWTH_FACTOR 256

/////////////////////////////////////////////////////////////////////////////
// CClusPropList class
/////////////////////////////////////////////////////////////////////////////

IMPLEMENT_DYNAMIC(CClusPropList, CObject)

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::CClusPropList
//
//Routine Description:
//Default constructor.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CClusPropList::CClusPropList(IN BOOL bAlwaysAddProp)
{
m_proplist.pList = NULL;
m_propCurrent.pb = NULL;
m_cbBufferSize = 0;
m_cbDataSize = 0;

m_bAlwaysAddProp = bAlwaysAddProp;

}  //*** CClusPropList::CClusPropList();

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::~CClusPropList
//
//Routine Description:
//Destructor.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CClusPropList::~CClusPropList(void)
{
delete [] m_proplist.pb;

}  //*** CClusPropList::~CClusPropList();

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::AddProp
//
//Routine Description:
//Add a string property to a property list if it has changed.
//
//Arguments:
//pwszName[IN] Name of the property.
//rstrValue[IN] Value of the property to set in the list.
//rstrPrevValue[IN] Previous value of the property.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusPropList::AddProp(
IN LPCWSTRpwszName,
IN const CString &rstrValue,
IN const CString &rstrPrevValue
)
{
PCLUSPROP_PROPERTY_NAMEpName;
PCLUSPROP_SZpValue;

ASSERT(pwszName != NULL);

if (m_bAlwaysAddProp || (rstrValue != rstrPrevValue))
{
DWORDcbNameSize;
DWORDcbValueSize;

// Calculate sizes and make sure we have a property list.
cbNameSize = sizeof(CLUSPROP_PROPERTY_NAME)
+ ALIGN_CLUSPROP((lstrlenW(pwszName) + 1) * sizeof(WCHAR));
cbValueSize = sizeof(CLUSPROP_SZ)
+ ALIGN_CLUSPROP((rstrValue.GetLength() + 1) * sizeof(WCHAR))
+ sizeof(CLUSPROP_SYNTAX); // value list endmark
AllocPropList(cbNameSize + cbValueSize);

// Set the property name.
pName = m_propCurrent.pName;
CopyProp(pName, CLUSPROP_TYPE_NAME, pwszName);
m_propCurrent.pb += cbNameSize;

// Set the property value.
pValue = m_propCurrent.pStringValue;
CopyProp(pValue, CLUSPROP_TYPE_LIST_VALUE, rstrValue);
m_propCurrent.pb += cbValueSize;

// Increment the property count and buffer size.
m_proplist.pList->nPropertyCount++;
m_cbDataSize += cbNameSize + cbValueSize;
}  // if:  the value has changed

}  //*** CClusPropList::AddProp(CString)

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::AddProp
//
//Routine Description:
//Add a DWORD property to a property list if it has changed.
//
//Arguments:
//pwszName[IN] Name of the property.
//dwValue[IN] Value of the property to set in the list.
//dwPrevValue[IN] Previous value of the property.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusPropList::AddProp(
IN LPCWSTRpwszName,
IN DWORDdwValue,
IN DWORDdwPrevValue
)
{
PCLUSPROP_PROPERTY_NAMEpName;
PCLUSPROP_DWORDpValue;

ASSERT(pwszName != NULL);

if (m_bAlwaysAddProp || (dwValue != dwPrevValue))
{
DWORDcbNameSize;
DWORDcbValueSize;

// Calculate sizes and make sure we have a property list.
cbNameSize = sizeof(CLUSPROP_PROPERTY_NAME)
+ ALIGN_CLUSPROP((lstrlenW(pwszName) + 1) * sizeof(WCHAR));
cbValueSize = sizeof(CLUSPROP_DWORD)
+ sizeof(CLUSPROP_SYNTAX); // value list endmark
AllocPropList(cbNameSize + cbValueSize);

// Set the property name.
pName = m_propCurrent.pName;
CopyProp(pName, CLUSPROP_TYPE_NAME, pwszName);
m_propCurrent.pb += cbNameSize;

// Set the property value.
pValue = m_propCurrent.pDwordValue;
CopyProp(pValue, CLUSPROP_TYPE_LIST_VALUE, dwValue);
m_propCurrent.pb += cbValueSize;

// Increment the property count and buffer size.
m_proplist.pList->nPropertyCount++;
m_cbDataSize += cbNameSize + cbValueSize;
}  // if:  the value has changed

}  //*** CClusPropList::AddProp(DWORD)

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::AddProp
//
//Routine Description:
//Add a binary property to a property list if it has changed.
//
//Arguments:
//pwszName[IN] Name of the property.
//pbValue[IN] Value of the property to set in the list.
//cbValue[IN] Count of bytes in pbValue.
//pbPrevValue[IN] Previous value of the property.
//cbPrevValue[IN] Count of bytes in pbPrevValue.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusPropList::AddProp(
IN LPCWSTRpwszName,
IN const PBYTEpbValue,
IN DWORDcbValue,
IN const PBYTEpbPrevValue,
IN DWORDcbPrevValue
)
{
BOOLbChanged = FALSE;
PCLUSPROP_PROPERTY_NAMEpName;
PCLUSPROP_BINARYpValue;

ASSERT(pwszName != NULL);
ASSERT(((cbValue == 0) && (cbPrevValue == 0)) || (pbValue != pbPrevValue));

// Determine if the buffer has changed.
if (m_bAlwaysAddProp || (cbValue != cbPrevValue))
bChanged = TRUE;
else if (!((cbValue == 0) && (cbPrevValue == 0)))
bChanged = memcmp(pbValue, pbPrevValue, cbValue) == 0;

if (bChanged)
{
DWORDcbNameSize;
DWORDcbValueSize;

// Calculate sizes and make sure we have a property list.
cbNameSize = sizeof(CLUSPROP_PROPERTY_NAME)
+ ALIGN_CLUSPROP((lstrlenW(pwszName) + 1) * sizeof(WCHAR));
cbValueSize = sizeof(CLUSPROP_BINARY)
+ ALIGN_CLUSPROP(cbValue)
+ sizeof(CLUSPROP_SYNTAX); // value list endmark
AllocPropList(cbNameSize + cbValueSize);

// Set the property name.
pName = m_propCurrent.pName;
CopyProp(pName, CLUSPROP_TYPE_NAME, pwszName);
m_propCurrent.pb += cbNameSize;

// Set the property value.
pValue = m_propCurrent.pBinaryValue;
CopyProp(pValue, CLUSPROP_TYPE_LIST_VALUE, pbValue, cbValue);
m_propCurrent.pb += cbValueSize;

// Increment the property count and buffer size.
m_proplist.pList->nPropertyCount++;
m_cbDataSize += cbNameSize + cbValueSize;
}  // if:  the value changed

}  //*** CClusPropList::AddProp(PBYTE)

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::CopyProp
//
//Routine Description:
//Copy a string property to a property structure.
//
//Arguments:
//pprop[OUT] Property structure to fill.
//proptype[IN] Type of string.
//pwsz[IN] String to copy.
//cbsz[IN] Count of bytes in pwsz string.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusPropList::CopyProp(
OUT PCLUSPROP_SZpprop,
IN CLUSTER_PROPERTY_TYPEproptype,
IN LPCWSTRpwsz,
IN DWORDcbsz
)
{
CLUSPROP_BUFFER_HELPERprops;

ASSERT(pprop != NULL);
ASSERT(pwsz != NULL);

pprop->Syntax.wFormat = CLUSPROP_FORMAT_SZ;
pprop->Syntax.wType = (WORD) proptype;
if (cbsz == 0)
cbsz = (lstrlenW(pwsz) + 1) * sizeof(WCHAR);
ASSERT(cbsz == (lstrlenW(pwsz) + 1) * sizeof(WCHAR));
pprop->cbLength = cbsz;
lstrcpyW(pprop->sz, pwsz);

// Set an endmark.
props.pStringValue = pprop;
props.pb += sizeof(*props.pStringValue) + ALIGN_CLUSPROP(cbsz);
props.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;

}  //*** CClusPropList::CopyProp(CString)

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::CopyProp
//
//Routine Description:
//Copy a DWORD property to a property structure.
//
//Arguments:
//pprop[OUT] Property structure to fill.
//proptype[IN] Type of DWORD.
//dw[IN] DWORD to copy.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusPropList::CopyProp(
OUT PCLUSPROP_DWORDpprop,
IN CLUSTER_PROPERTY_TYPEproptype,
IN DWORDdw
)
{
CLUSPROP_BUFFER_HELPERprops;

ASSERT(pprop != NULL);

pprop->Syntax.wFormat = CLUSPROP_FORMAT_DWORD;
pprop->Syntax.wType = (WORD) proptype;
pprop->cbLength = sizeof(DWORD);
pprop->dw = dw;

// Set an endmark.
props.pDwordValue = pprop;
props.pb += sizeof(*props.pDwordValue);
props.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;

}  //*** CClusPropList::CopyProp(DWORD)

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::CopyProp
//
//Routine Description:
//Copy a binary property to a property structure.
//
//Arguments:
//pprop[OUT] Property structure to fill.
//proptype[IN] Type of string.
//pb[IN] Block to copy.
//cbsz[IN] Count of bytes in pb buffer.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusPropList::CopyProp(
OUT PCLUSPROP_BINARYpprop,
IN CLUSTER_PROPERTY_TYPEproptype,
IN const PBYTEpb,
IN DWORDcb
)
{
CLUSPROP_BUFFER_HELPERprops;

ASSERT(pprop != NULL);

pprop->Syntax.wFormat = CLUSPROP_FORMAT_BINARY;
pprop->Syntax.wType = (WORD) proptype;
pprop->cbLength = cb;
if (cb > 0)
CopyMemory(pprop->rgb, pb, cb);

// Set an endmark.
props.pBinaryValue = pprop;
props.pb += sizeof(*props.pStringValue) + ALIGN_CLUSPROP(cb);
props.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;

}  //*** CClusPropList::CopyProp(PBYTE)

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::AllocPropList
//
//Routine Description:
//Allocate a property list buffer that's big enough to hold the next
//property.
//
//Arguments:
//cbMinimum[IN] Minimum size of the property list.
//
//Return Value:
//None.
//
//Exceptions Thrown:
//Any exceptions thrown by BYTE::operator new().
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusPropList::AllocPropList(
IN DWORDcbMinimum
)
{
DWORDcbTotal;

ASSERT(cbMinimum > 0);

// Add the size of the item count and final endmark.
cbMinimum += sizeof(DWORD) + sizeof(CLUSPROP_SYNTAX);
cbTotal = m_cbDataSize + cbMinimum;

if (m_cbBufferSize < cbTotal)
{
PBYTEpbNewProplist;

cbMinimum = max(BUFFER_GROWTH_FACTOR, cbMinimum);
cbTotal = m_cbDataSize + cbMinimum;

// Allocate and zero a new buffer.
pbNewProplist = new BYTE[cbTotal];
ZeroMemory(pbNewProplist, cbTotal);

// If there was a previous buffer, copy it and the delete it.
if (m_proplist.pb != NULL)
{
if (m_cbDataSize != 0)
CopyMemory(pbNewProplist, m_proplist.pb, m_cbDataSize);
delete [] m_proplist.pb;
m_propCurrent.pb = pbNewProplist + (m_propCurrent.pb - m_proplist.pb);
}  // if:  there was a previous buffer
else
m_propCurrent.pb = pbNewProplist + sizeof(DWORD); // move past prop count

// Save the new buffer.
m_proplist.pb = pbNewProplist;
m_cbBufferSize = cbTotal;
}  // if:  buffer isn't big enough

}  //*** CClusPropList::AllocPropList(PBYTE)

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::DwGetNodeProperties
//
//Routine Description:
//Get properties on a node.
//
//Arguments:
//hNode[IN] Handle for the node to get properties from.
//dwControlCode[IN] Control code for the request.
//hHostNode[IN] Handle for the node to direct this request to.
//Defaults to NULL.
//lpInBuffer[IN] Input buffer for the request.  Defaults to NULL.
//cbInBufferSize[IN] Size of the input buffer.  Defaults to 0.
//
//Return Value:
//None.
//
//Exceptions Thrown:
//Any exceptions CClusPropList::AllocPropList().
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusPropList::DwGetNodeProperties(
IN HNODEhNode,
IN DWORDdwControlCode,
IN HNODEhHostNode,
IN LPVOIDlpInBuffer,
IN DWORDcbInBufferSize
)
{
DWORDdwStatus;
DWORDcbProps= 256;

ASSERT(hNode != NULL);
ASSERT((dwControlCode & (CLUSCTL_OBJECT_MASK << CLUSCTL_OBJECT_SHIFT))
== (CLUS_OBJECT_NODE << CLUSCTL_OBJECT_SHIFT));

ASSERT(m_proplist.pb == NULL);
ASSERT(m_propCurrent.pb == NULL);
ASSERT(m_cbBufferSize == 0);
ASSERT(m_cbDataSize == 0);

do
{
// Allocate a default-sized buffer.
try
{
AllocPropList(cbProps);
}  // try
catch (CMemoryException * pme)
{
pme->Delete();
return ERROR_NOT_ENOUGH_MEMORY;
}  // catch:  CMemoryException

// Get properties.
dwStatus = ClusterNodeControl(
hNode,
hHostNode,
dwControlCode,
lpInBuffer,
cbInBufferSize,
m_proplist.pb,
m_cbBufferSize,
&cbProps
);
} while (dwStatus == ERROR_MORE_DATA);

if (dwStatus != ERROR_SUCCESS)
{
delete [] m_proplist.pb;
m_proplist.pb = NULL;
m_propCurrent.pb = NULL;
m_cbBufferSize = 0;
m_cbDataSize = 0;
}  // if:  error getting properties.
else
m_cbDataSize = cbProps;

return dwStatus;

}  //*** CClusPropList::DwGetNodeProperties()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::DwGetGroupProperties
//
//Routine Description:
//Get properties on a group.
//
//Arguments:
//hGroup[IN] Handle for the group to get properties from.
//dwControlCode[IN] Control code for the request.
//hHostNode[IN] Handle for the node to direct this request to.
//Defaults to NULL.
//lpInBuffer[IN] Input buffer for the request.  Defaults to NULL.
//cbInBufferSize[IN] Size of the input buffer.  Defaults to 0.
//
//Return Value:
//None.
//
//Exceptions Thrown:
//Any exceptions CClusPropList::AllocPropList().
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusPropList::DwGetGroupProperties(
IN HGROUPhGroup,
IN DWORDdwControlCode,
IN HNODEhHostNode,
IN LPVOIDlpInBuffer,
IN DWORDcbInBufferSize
)
{
DWORDdwStatus;
DWORDcbProps= 256;

ASSERT(hGroup != NULL);
ASSERT((dwControlCode & (CLUSCTL_OBJECT_MASK << CLUSCTL_OBJECT_SHIFT))
== (CLUS_OBJECT_GROUP << CLUSCTL_OBJECT_SHIFT));

ASSERT(m_proplist.pb == NULL);
ASSERT(m_propCurrent.pb == NULL);
ASSERT(m_cbBufferSize == 0);
ASSERT(m_cbDataSize == 0);

do
{
// Allocate a default-sized buffer.
try
{
AllocPropList(cbProps);
}  // try
catch (CMemoryException * pme)
{
pme->Delete();
return ERROR_NOT_ENOUGH_MEMORY;
}  // catch:  CMemoryException

// Get properties.
dwStatus = ClusterGroupControl(
hGroup,
hHostNode,
dwControlCode,
lpInBuffer,
cbInBufferSize,
m_proplist.pb,
m_cbBufferSize,
&cbProps
);
} while (dwStatus == ERROR_MORE_DATA);

if (dwStatus != ERROR_SUCCESS)
{
delete [] m_proplist.pb;
m_proplist.pb = NULL;
m_propCurrent.pb = NULL;
m_cbBufferSize = 0;
m_cbDataSize = 0;
}  // if:  error getting properties.
else
m_cbDataSize = cbProps;

return dwStatus;

}  //*** CClusPropList::DwGetGroupProperties()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::DwGetResourceProperties
//
//Routine Description:
//Get properties on a resource.
//
//Arguments:
//hResource[IN] Handle for the resource to get properties from.
//dwControlCode[IN] Control code for the request.
//hHostNode[IN] Handle for the node to direct this request to.
//Defaults to NULL.
//lpInBuffer[IN] Input buffer for the request.  Defaults to NULL.
//cbInBufferSize[IN] Size of the input buffer.  Defaults to 0.
//
//Return Value:
//None.
//
//Exceptions Thrown:
//Any exceptions CClusPropList::AllocPropList().
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusPropList::DwGetResourceProperties(
IN HRESOURCEhResource,
IN DWORDdwControlCode,
IN HNODEhHostNode,
IN LPVOIDlpInBuffer,
IN DWORDcbInBufferSize
)
{
DWORDdwStatus;
DWORDcbProps= 256;

ASSERT(hResource != NULL);
ASSERT((dwControlCode & (CLUSCTL_OBJECT_MASK << CLUSCTL_OBJECT_SHIFT))
== (CLUS_OBJECT_RESOURCE << CLUSCTL_OBJECT_SHIFT));

ASSERT(m_proplist.pb == NULL);
ASSERT(m_propCurrent.pb == NULL);
ASSERT(m_cbBufferSize == 0);
ASSERT(m_cbDataSize == 0);

do
{
// Allocate a default-sized buffer.
try
{
AllocPropList(cbProps);
}  // try
catch (CMemoryException * pme)
{
pme->Delete();
return ERROR_NOT_ENOUGH_MEMORY;
}  // catch:  CMemoryException

// Get properties.
dwStatus = ClusterResourceControl(
hResource,
hHostNode,
dwControlCode,
lpInBuffer,
cbInBufferSize,
m_proplist.pb,
m_cbBufferSize,
&cbProps
);
} while (dwStatus == ERROR_MORE_DATA);

if (dwStatus != ERROR_SUCCESS)
{
delete [] m_proplist.pb;
m_proplist.pb = NULL;
m_propCurrent.pb = NULL;
m_cbBufferSize = 0;
m_cbDataSize = 0;
}  // if:  error getting properties.
else
m_cbDataSize = cbProps;

return dwStatus;

}  //*** CClusPropList::DwGetResourceProperties()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::DwGetResourceTypeProperties
//
//Routine Description:
//Get properties on a resource type.
//
//Arguments:
//hCluster[IN] Handle for the cluster in which the resource
//type resides.
//pwszResTypeName[IN] Name of the resource type.
//dwControlCode[IN] Control code for the request.
//hHostNode[IN] Handle for the node to direct this request to.
//Defaults to NULL.
//lpInBuffer[IN] Input buffer for the request.  Defaults to NULL.
//cbInBufferSize[IN] Size of the input buffer.  Defaults to 0.
//
//Return Value:
//None.
//
//Exceptions Thrown:
//Any exceptions CClusPropList::AllocPropList().
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusPropList::DwGetResourceTypeProperties(
IN HCLUSTERhCluster,
IN LPCWSTRpwszResTypeName,
IN DWORDdwControlCode,
IN HNODEhHostNode,
IN LPVOIDlpInBuffer,
IN DWORDcbInBufferSize
)
{
DWORDdwStatus;
DWORDcbProps= 256;

ASSERT(hCluster != NULL);
ASSERT(pwszResTypeName != NULL);
ASSERT(*pwszResTypeName != L'\0');
ASSERT((dwControlCode & (CLUSCTL_OBJECT_MASK << CLUSCTL_OBJECT_SHIFT))
== (CLUS_OBJECT_RESOURCE_TYPE << CLUSCTL_OBJECT_SHIFT));

ASSERT(m_proplist.pb == NULL);
ASSERT(m_propCurrent.pb == NULL);
ASSERT(m_cbBufferSize == 0);
ASSERT(m_cbDataSize == 0);

do
{
// Allocate a default-sized buffer.
try
{
AllocPropList(cbProps);
}  // try
catch (CMemoryException * pme)
{
pme->Delete();
return ERROR_NOT_ENOUGH_MEMORY;
}  // catch:  CMemoryException

// Get properties.
dwStatus = ClusterResourceTypeControl(
hCluster,
pwszResTypeName,
hHostNode,
dwControlCode,
lpInBuffer,
cbInBufferSize,
m_proplist.pb,
m_cbBufferSize,
&cbProps
);
} while (dwStatus == ERROR_MORE_DATA);

if (dwStatus != ERROR_SUCCESS)
{
delete [] m_proplist.pb;
m_proplist.pb = NULL;
m_propCurrent.pb = NULL;
m_cbBufferSize = 0;
m_cbDataSize = 0;
}  // if:  error getting properties.
else
m_cbDataSize = cbProps;

return dwStatus;

}  //*** CClusPropList::DwGetResourceTypeProperties()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::DwGetNetworkProperties
//
//Routine Description:
//Get properties on a network.
//
//Arguments:
//hNetwork[IN] Handle for the network to get properties from.
//dwControlCode[IN] Control code for the request.
//hHostNode[IN] Handle for the node to direct this request to.
//Defaults to NULL.
//lpInBuffer[IN] Input buffer for the request.  Defaults to NULL.
//cbInBufferSize[IN] Size of the input buffer.  Defaults to 0.
//
//Return Value:
//None.
//
//Exceptions Thrown:
//Any exceptions CClusPropList::AllocPropList().
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusPropList::DwGetNetworkProperties(
IN HNETWORKhNetwork,
IN DWORDdwControlCode,
IN HNODEhHostNode,
IN LPVOIDlpInBuffer,
IN DWORDcbInBufferSize
)
{
DWORDdwStatus;
DWORDcbProps= 256;

ASSERT(hNetwork != NULL);
ASSERT((dwControlCode & (CLUSCTL_OBJECT_MASK << CLUSCTL_OBJECT_SHIFT))
== (CLUS_OBJECT_NETWORK << CLUSCTL_OBJECT_SHIFT));

ASSERT(m_proplist.pb == NULL);
ASSERT(m_propCurrent.pb == NULL);
ASSERT(m_cbBufferSize == 0);
ASSERT(m_cbDataSize == 0);

do
{
// Allocate a default-sized buffer.
try
{
AllocPropList(cbProps);
}  // try
catch (CMemoryException * pme)
{
pme->Delete();
return ERROR_NOT_ENOUGH_MEMORY;
}  // catch:  CMemoryException

// Get properties.
dwStatus = ClusterNetworkControl(
hNetwork,
hHostNode,
dwControlCode,
lpInBuffer,
cbInBufferSize,
m_proplist.pb,
m_cbBufferSize,
&cbProps
);
} while (dwStatus == ERROR_MORE_DATA);

if (dwStatus != ERROR_SUCCESS)
{
delete [] m_proplist.pb;
m_proplist.pb = NULL;
m_propCurrent.pb = NULL;
m_cbBufferSize = 0;
m_cbDataSize = 0;
}  // if:  error getting private properties.
else
m_cbDataSize = cbProps;

return dwStatus;

}  //*** CClusPropList::DwGetNetworkProperties()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CClusPropList::DwGetNetInterfaceProperties
//
//Routine Description:
//Get properties on a network interface.
//
//Arguments:
//hNetInterface[IN] Handle for the network interface to get properties from.
//dwControlCode[IN] Control code for the request.
//hHostNode[IN] Handle for the node to direct this request to.
//Defaults to NULL.
//lpInBuffer[IN] Input buffer for the request.  Defaults to NULL.
//cbInBufferSize[IN] Size of the input buffer.  Defaults to 0.
//
//Return Value:
//None.
//
//Exceptions Thrown:
//Any exceptions CClusPropList::AllocPropList().
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusPropList::DwGetNetInterfaceProperties(
IN HNETINTERFACEhNetInterface,
IN DWORDdwControlCode,
IN HNODEhHostNode,
IN LPVOIDlpInBuffer,
IN DWORDcbInBufferSize
)
{
DWORDdwStatus;
DWORDcbProps= 256;

ASSERT(hNetInterface != NULL);
ASSERT((dwControlCode & (CLUSCTL_OBJECT_MASK << CLUSCTL_OBJECT_SHIFT))
== (CLUS_OBJECT_NETINTERFACE << CLUSCTL_OBJECT_SHIFT));

ASSERT(m_proplist.pb == NULL);
ASSERT(m_propCurrent.pb == NULL);
ASSERT(m_cbBufferSize == 0);
ASSERT(m_cbDataSize == 0);

do
{
// Allocate a default-sized buffer.
try
{
AllocPropList(cbProps);
}  // try
catch (CMemoryException * pme)
{
pme->Delete();
return ERROR_NOT_ENOUGH_MEMORY;
}  // catch:  CMemoryException

// Get properties.
dwStatus = ClusterNetInterfaceControl(
hNetInterface,
hHostNode,
dwControlCode,
lpInBuffer,
cbInBufferSize,
m_proplist.pb,
m_cbBufferSize,
&cbProps
);
} while (dwStatus == ERROR_MORE_DATA);

if (dwStatus != ERROR_SUCCESS)
{
delete [] m_proplist.pb;
m_proplist.pb = NULL;
m_propCurrent.pb = NULL;
m_cbBufferSize = 0;
m_cbDataSize = 0;
}  // if:  error getting private properties.
else
m_cbDataSize = cbProps;

return dwStatus;

}  //*** CClusPropList::DwGetNetInterfaceProperties()


PROPLIST.H

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//PropList.h
//
//Abstract:
//Definition of the CClusPropList class.
//
//Implementation File:
//PropList.cpp
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#ifndef _PROPLIST_H_
#define _PROPLIST_H_

/////////////////////////////////////////////////////////////////////////////
// Include Files
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// Forward Class Declarations
/////////////////////////////////////////////////////////////////////////////

class CClusPropList;

/////////////////////////////////////////////////////////////////////////////
// External Class Declarations
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// CObjectProperty
/////////////////////////////////////////////////////////////////////////////

#pragma warning( disable : 4201 ) // nonstandard extension used : nameless struct/union

class CObjectProperty
{
public:
LPCWSTRm_pwszName;
CLUSTER_PROPERTY_FORMATm_propFormat;

union CValue
{
CString *pstr;
DWORD *pdw;
BOOL *pb;
struct
{
PBYTE *ppb;
DWORD *pcb;
};
};
CValuem_value;
CValuem_valuePrev;

voidSet(
IN LPCWSTR pwszName,
IN CString & rstrValue,
IN CString & rstrPrevValue
)
{
m_pwszName = pwszName;
m_propFormat = CLUSPROP_FORMAT_SZ;
m_value.pstr = &rstrValue;
m_value.pcb = NULL;
m_valuePrev.pstr = &rstrPrevValue;
m_valuePrev.pcb = NULL;
}

voidSet(
IN LPCWSTR pwszName,
IN DWORD & rdwValue,
IN DWORD & rdwPrevValue
)
{
m_pwszName = pwszName;
m_propFormat = CLUSPROP_FORMAT_DWORD;
m_value.pdw = &rdwValue;
m_value.pcb = NULL;
m_valuePrev.pdw = &rdwPrevValue;
m_valuePrev.pcb = NULL;
}

voidSet(
IN LPCWSTR pwszName,
IN BOOL & rbValue,
IN BOOL & rbPrevValue
)
{
m_pwszName = pwszName;
m_propFormat = CLUSPROP_FORMAT_DWORD;
m_value.pb = &rbValue;
m_value.pcb = NULL;
m_valuePrev.pb = &rbPrevValue;
m_valuePrev.pcb = NULL;
}

voidSet(
IN LPCWSTR pwszName,
IN PBYTE & rpbValue,
IN DWORD & rcbValue,
IN PBYTE & rpbPrevValue,
IN DWORD & rcbPrevValue
)
{
m_pwszName = pwszName;
m_propFormat = CLUSPROP_FORMAT_BINARY;
m_value.ppb = &rpbValue;
m_value.pcb = &rcbValue;
m_valuePrev.ppb = &rpbPrevValue;
m_valuePrev.pcb = &rcbPrevValue;
}

voidSet(
IN LPCWSTR pwszName,
IN LPWSTR & rpwszValue,
IN DWORD & rcbValue,
IN LPWSTR & rpwszPrevValue,
IN DWORD & rcbPrevValue
)
{
m_pwszName = pwszName;
m_propFormat = CLUSPROP_FORMAT_MULTI_SZ;
m_value.ppb = (PBYTE *) &rpwszValue;
m_value.pcb = &rcbValue;
m_valuePrev.ppb = (PBYTE *) &rpwszPrevValue;
m_valuePrev.pcb = &rcbPrevValue;
}

};  //*** class CObjectProperty

/////////////////////////////////////////////////////////////////////////////
// CClusPropList dialog
/////////////////////////////////////////////////////////////////////////////

class CClusPropList : public CObject
{
DECLARE_DYNAMIC(CClusPropList);

// Construction
public:
CClusPropList(IN BOOL bAlwaysAddProp = FALSE);
~CClusPropList(void);

// Attributes
protected:
BOOLm_bAlwaysAddProp;

CLUSPROP_BUFFER_HELPERm_proplist;
CLUSPROP_BUFFER_HELPERm_propCurrent;
DWORDm_cbBufferSize;
DWORDm_cbDataSize;

public:
const CLUSPROP_BUFFER_HELPER const *Proplist(void) const{ return &m_proplist; }
PBYTEPbProplist(void) const{ return m_proplist.pb; }
DWORDCbProplist(void) const{ return m_cbDataSize + sizeof(CLUSPROP_SYNTAX); /*endmark*/ }
DWORDCprops(void) const
{
if (m_proplist.pb == NULL)
return 0;
return m_proplist.pList->nPropertyCount;
}

voidAddProp(
IN LPCWSTRpwszName,
IN const CString &rstrValue,
IN const CString &rstrPrevValue
);
voidAddProp(
IN LPCWSTRpwszName,
IN DWORDdwValue,
IN DWORDdwPrevValue
);
voidAddProp(
IN LPCWSTRpwszName,
IN const PBYTEpbValue,
IN DWORDcbValue,
IN const PBYTEpbPrevValue,
IN DWORDcbPrevValue
);

voidAllocPropList(IN DWORD cbMinimum);

// Operations
public:
DWORDDwGetNodeProperties(
IN HNODEhNode,
IN DWORDdwControlCode,
IN HNODEhHostNode= NULL,
IN LPVOIDlpInBuffer= NULL,
IN DWORDcbInBufferSize= 0
);

DWORDDwGetGroupProperties(
IN HGROUPhGroup,
IN DWORDdwControlCode,
IN HNODEhHostNode= NULL,
IN LPVOIDlpInBuffer= NULL,
IN DWORDcbInBufferSize= 0
);

DWORDDwGetResourceProperties(
IN HRESOURCEhResource,
IN DWORDdwControlCode,
IN HNODEhHostNode= NULL,
IN LPVOIDlpInBuffer= NULL,
IN DWORDcbInBufferSize= 0
);

DWORDDwGetResourceTypeProperties(
IN HCLUSTERhCluster,
IN LPCWSTRpwszResTypeName,
IN DWORDdwControlCode,
IN HNODEhHostNode= NULL,
IN LPVOIDlpInBuffer= NULL,
IN DWORDcbInBufferSize= 0
);

DWORDDwGetNetworkProperties(
IN HNETWORKhNetwork,
IN DWORDdwControlCode,
IN HNODEhHostNode= NULL,
IN LPVOIDlpInBuffer= NULL,
IN DWORDcbInBufferSize= 0
);

DWORDDwGetNetInterfaceProperties(
IN HNETINTERFACEhNetInterface,
IN DWORDdwControlCode,
IN HNODEhHostNode= NULL,
IN LPVOIDlpInBuffer= NULL,
IN DWORDcbInBufferSize= 0
);

// Overrides

// Implementation
protected:
voidCopyProp(
IN PCLUSPROP_SZpprop,
IN CLUSTER_PROPERTY_TYPEproptype,
IN LPCWSTRpwsz,
IN DWORDcbsz = 0
);
voidCopyProp(
IN PCLUSPROP_DWORDpprop,
IN CLUSTER_PROPERTY_TYPEproptype,
IN DWORDdw
);
voidCopyProp(
IN PCLUSPROP_BINARYpprop,
IN CLUSTER_PROPERTY_TYPEproptype,
IN const PBYTEpb,
IN DWORDcb
);

DWORDDwGetPrivateProps(
OUT PBYTE *ppbProps,
IN CLUSTER_CONTROL_OBJECTccobjtype = CLUS_OBJECT_RESOURCE
);
DWORDDwSetPrivateProps(
IN PBYTEpbProps,
IN DWORDcbProps,
IN CLUSTER_CONTROL_OBJECTccobjtype = CLUS_OBJECT_RESOURCE
);

};  //*** class CClusPropList

#pragma warning( default : 4201 )

/////////////////////////////////////////////////////////////////////////////

#endif // _PROPLIST_H_


BASEPAGE.CPP

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//BasePage.cpp
//
//Abstract:
//Implementation of the CBasePropertyPage class.
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <clusapi.h>
#include "SmbSmpEx.h"
#include "ExtObj.h"
#include "BasePage.h"
#include "BasePage.inl"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CBasePropertyPage property page
/////////////////////////////////////////////////////////////////////////////

IMPLEMENT_DYNCREATE(CBasePropertyPage, CPropertyPage)

/////////////////////////////////////////////////////////////////////////////
// Message Maps

BEGIN_MESSAGE_MAP(CBasePropertyPage, CPropertyPage)
//{{AFX_MSG_MAP(CBasePropertyPage)
ON_WM_CREATE()
ON_WM_DESTROY()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::CBasePropertyPage
//
//Routine Description:
//Default constructor.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CBasePropertyPage::CBasePropertyPage(void)
{
CommonConstruct();

}  //*** CBasePropertyPage::CBasePropertyPage()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::CBasePropertyPage
//
//Routine Description:
//Default constructor.
//
//Arguments:
//nIDTemplate[IN] Dialog template resource ID.
//nIDCaption[IN] Caption string resource ID.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CBasePropertyPage::CBasePropertyPage(
IN UINTnIDTemplate,
IN UINTnIDCaption
)
: CPropertyPage(nIDTemplate, nIDCaption)
{
CommonConstruct();

}  //*** CBasePropertyPage::CBasePropertyPage(UINT, UINT)

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::CommonConstruct
//
//Routine Description:
//Common construction.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CBasePropertyPage::CommonConstruct(void)
{
//{{AFX_DATA_INIT(CBasePropertyPage)
//}}AFX_DATA_INIT

m_peo = NULL;
m_hpage = NULL;
m_bBackPressed = FALSE;

m_iddPropertyPage = NULL;
m_iddWizardPage = NULL;
m_idsCaption = NULL;

}  //*** CBasePropertyPage::CommonConstruct()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::BInit
//
//Routine Description:
//Initialize the page.
//
//Arguments:
//peo[IN OUT] Pointer to the extension object.
//
//Return Value:
//TRUEPage initialized successfully.
//FALSEPage failed to initialize.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CBasePropertyPage::BInit(IN OUT CExtObject * peo)
{
ASSERT(peo != NULL);

AFX_MANAGE_STATE(AfxGetStaticModuleState());

CWaitCursorwc;

m_peo = peo;

// Don't display a help button.
m_psp.dwFlags &= ~PSP_HASHELP;

// Construct the property page.
if (Peo()->BWizard())
{
ASSERT(IddWizardPage() != NULL);
Construct(IddWizardPage(), IdsCaption());
}  // if:  adding page to wizard
else
{
ASSERT(IddPropertyPage() != NULL);
Construct(IddPropertyPage(), IdsCaption());
}  // else:  adding page to property sheet

// Read the properties private to this resource and parse them.
{
DWORDdwStatus;
CClusPropListcpl;

ASSERT(Peo() != NULL);
ASSERT(Peo()->PodObjData());

// Read the properties.
switch (Cot())
{
case CLUADMEX_OT_NODE:
ASSERT(Peo()->PndNodeData()->m_hnode != NULL);
dwStatus = cpl.DwGetNodeProperties(
Peo()->PndNodeData()->m_hnode,
CLUSCTL_NODE_GET_PRIVATE_PROPERTIES
);
break;
case CLUADMEX_OT_GROUP:
ASSERT(Peo()->PgdGroupData()->m_hgroup != NULL);
dwStatus = cpl.DwGetGroupProperties(
Peo()->PgdGroupData()->m_hgroup,
CLUSCTL_GROUP_GET_PRIVATE_PROPERTIES
);
break;
case CLUADMEX_OT_RESOURCE:
ASSERT(Peo()->PrdResData()->m_hresource != NULL);
dwStatus = cpl.DwGetResourceProperties(
Peo()->PrdResData()->m_hresource,
CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES
);
break;
case CLUADMEX_OT_RESOURCETYPE:
ASSERT(Peo()->PodObjData()->m_strName.GetLength() > 0);
dwStatus = cpl.DwGetResourceTypeProperties(
Hcluster(),
Peo()->PodObjData()->m_strName,
CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_PROPERTIES
);
break;
case CLUADMEX_OT_NETWORK:
ASSERT(Peo()->PndNetworkData()->m_hnetwork != NULL);
dwStatus = cpl.DwGetNetworkProperties(
Peo()->PndNetworkData()->m_hnetwork,
CLUSCTL_NETWORK_GET_PRIVATE_PROPERTIES
);
break;
case CLUADMEX_OT_NETINTERFACE:
ASSERT(Peo()->PndNetInterfaceData()->m_hnetinterface != NULL);
dwStatus = cpl.DwGetNetInterfaceProperties(
Peo()->PndNetInterfaceData()->m_hnetinterface,
CLUSCTL_NETINTERFACE_GET_PRIVATE_PROPERTIES
);
break;
default:
ASSERT(0);
}  // switch:  object type

// Parse the properties.
if (dwStatus == ERROR_SUCCESS)
{
// Parse the properties.
try
{
dwStatus = DwParseProperties(cpl);
}  // try
catch (CMemoryException * pme)
{
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
pme->Delete();
}  // catch:  CMemoryException
}  // if:  properties read successfully

if (dwStatus != ERROR_SUCCESS)
{
return FALSE;
}  // if:  error parsing getting or parsing properties
}  // Read the properties private to this resource and parse them

return TRUE;

}  //*** CBasePropertyPage::BInit()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::DwParseProperties
//
//Routine Description:
//Parse the properties of the resource.  This is in a separate function
//from BInit so that the optimizer can do a better job.
//
//Arguments:
//rcpl[IN] Cluster property list to parse.
//
//Return Value:
//ERROR_SUCCESSProperties were parsed successfully.
//
//Exceptions Thrown:
//Any exceptions from CString::operator=().
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CBasePropertyPage::DwParseProperties(IN const CClusPropList & rcpl)
{
DWORDcProps;
DWORDcprop;
DWORDcbProps;
const CObjectProperty *pprop;
CLUSPROP_BUFFER_HELPERprops;
CLUSPROP_PROPERTY_NAME const *pName;

ASSERT(rcpl.PbProplist() != NULL);

props.pb = rcpl.PbProplist();
cbProps = rcpl.CbProplist();

// Loop through each property.
for (cProps = *(props.pdw++) ; cProps > 0 ; cProps--)
{
pName = props.pName;
ASSERT(pName->Syntax.dw == CLUSPROP_SYNTAX_NAME);
props.pb += sizeof(*pName) + ALIGN_CLUSPROP(pName->cbLength);

// Decrement the counter by the size of the name.
ASSERT(cbProps > sizeof(*pName) + ALIGN_CLUSPROP(pName->cbLength));
cbProps -= sizeof(*pName) + ALIGN_CLUSPROP(pName->cbLength);

ASSERT(cbProps > sizeof(*props.pValue) + ALIGN_CLUSPROP(props.pValue->cbLength));

// Parse known properties.
for (pprop = Pprops(), cprop = Cprops() ; cprop > 0 ; pprop++, cprop--)
{
if (lstrcmpiW(pName->sz, pprop->m_pwszName) == 0)
{
ASSERT(props.pSyntax->wFormat == pprop->m_propFormat);
switch (pprop->m_propFormat)
{
case CLUSPROP_FORMAT_SZ:
case CLUSPROP_FORMAT_EXPAND_SZ:
ASSERT((props.pValue->cbLength == (lstrlenW(props.pStringValue->sz) + 1) * sizeof(WCHAR))
|| ((props.pValue->cbLength == 0) && (props.pStringValue->sz[0] == L'\0')));
*pprop->m_value.pstr = props.pStringValue->sz;
*pprop->m_valuePrev.pstr = props.pStringValue->sz;
break;
case CLUSPROP_FORMAT_DWORD:
ASSERT(props.pValue->cbLength == sizeof(DWORD));
*pprop->m_value.pdw = props.pDwordValue->dw;
*pprop->m_valuePrev.pdw = props.pDwordValue->dw;
break;
case CLUSPROP_FORMAT_BINARY:
case CLUSPROP_FORMAT_MULTI_SZ:
*pprop->m_value.ppb = props.pBinaryValue->rgb;
*pprop->m_value.pcb = props.pBinaryValue->cbLength;
*pprop->m_valuePrev.ppb = props.pBinaryValue->rgb;
*pprop->m_valuePrev.pcb = props.pBinaryValue->cbLength;
break;
default:
ASSERT(0);// don't know how to deal with this type
}  // switch:  property format

// Exit the loop since we found the parameter.
break;
}  // if:  found a match
}  // for:  each property

// If the property wasn't known, ask the derived class to parse it.
if (cprop == 0)
{
DWORDdwStatus;

dwStatus = DwParseUnknownProperty(pName->sz, props, cbProps);
if (dwStatus != ERROR_SUCCESS)
return dwStatus;
}  // if:  property not parsed

// Advance the buffer pointer past the value in the value list.
while ((props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK)
&& (cbProps > 0))
{
ASSERT(cbProps > sizeof(*props.pValue) + ALIGN_CLUSPROP(props.pValue->cbLength));
cbProps -= sizeof(*props.pValue) + ALIGN_CLUSPROP(props.pValue->cbLength);
props.pb += sizeof(*props.pValue) + ALIGN_CLUSPROP(props.pValue->cbLength);
}  // while:  more values in the list

// Advance the buffer pointer past the value list endmark.
ASSERT(cbProps >= sizeof(*props.pSyntax));
cbProps -= sizeof(*props.pSyntax);
props.pb += sizeof(*props.pSyntax); // endmark
}  // for:  each property

return ERROR_SUCCESS;

}  //*** CBasePropertyPage::DwParseProperties()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::OnCreate
//
//Routine Description:
//Handler for the WM_CREATE message.
//
//Arguments:
//lpCreateStruct[IN OUT] Window create structure.
//
//Return Value:
//-1Error.
//0Success.
//
//--
/////////////////////////////////////////////////////////////////////////////
int CBasePropertyPage::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

// Attach the window to the property page structure.
// This has been done once already in the main application, since the
// main application owns the property sheet.  It needs to be done here
// so that the window handle can be found in the DLL's handle map.
HWND hWnd = m_hWnd;
m_hWnd = NULL;
Attach(hWnd);

return CPropertyPage::OnCreate(lpCreateStruct);

}  //*** CBasePropertyPage::OnCreate()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::OnDestroy
//
//Routine Description:
//Handler for the WM_DESTROY message.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CBasePropertyPage::OnDestroy(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

// Detach the window from the property page structure.
// This will be done again by the main application, since it owns the
// property sheet.  It needs to be done here so that the window handle
// can be removed from the DLL's handle map.
if (m_hWnd != NULL)
{
HWND hWnd = m_hWnd;

Detach();
m_hWnd = hWnd;
}

CPropertyPage::OnDestroy();

}  //*** CBasePropertyPage::OnDestroy()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::DoDataExchange
//
//Routine Description:
//Do data exchange between the dialog and the class.
//
//Arguments:
//pDX[IN OUT] Data exchange object
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CBasePropertyPage::DoDataExchange(CDataExchange * pDX)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

//{{AFX_DATA_MAP(CBasePropertyPage)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
DDX_Control(pDX, IDC_PP_ICON, m_staticIcon);
DDX_Control(pDX, IDC_PP_TITLE, m_staticTitle);

if (pDX->m_bSaveAndValidate)
{
if (!BBackPressed())
{
CWaitCursorwc;

// Validate the data.
if (!BSetPrivateProps(TRUE /*bValidateOnly*/))
pDX->Fail();
}  // if:  Back button not pressed
}  // if:  saving data from dialog
else
{
// Set the title.
DDX_Text(pDX, IDC_PP_TITLE, m_strTitle);
}  // if:  not saving data

CPropertyPage::DoDataExchange(pDX);

}  //*** CBasePropertyPage::DoDataExchange()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::OnInitDialog
//
//Routine Description:
//Handler for the WM_INITDIALOG message.
//
//Arguments:
//None.
//
//Return Value:
//TRUEWe need the focus to be set for us.
//FALSEWe already set the focus to the proper control.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CBasePropertyPage::OnInitDialog(void)
{
ASSERT(Peo() != NULL);
ASSERT(Peo()->PodObjData() != NULL);

AFX_MANAGE_STATE(AfxGetStaticModuleState());

// Set the title string.
m_strTitle = Peo()->PodObjData()->m_strName;

// Call the base class method.
CPropertyPage::OnInitDialog();

// Display an icon for the object.
if (Peo()->Hicon() != NULL)
m_staticIcon.SetIcon(Peo()->Hicon());

return TRUE;// return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE

}  //*** CBasePropertyPage::OnInitDialog()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::OnSetActive
//
//Routine Description:
//Handler for the PSN_SETACTIVE message.
//
//Arguments:
//None.
//
//Return Value:
//TRUEPage successfully initialized.
//FALSEPage not initialized.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CBasePropertyPage::OnSetActive(void)
{
HRESULThr;

ASSERT(Peo() != NULL);
ASSERT(Peo()->PodObjData() != NULL);

AFX_MANAGE_STATE(AfxGetStaticModuleState());

// Reread the data.
hr = Peo()->HrGetObjectInfo();
if (hr != NOERROR)
return FALSE;

// Set the title string.
m_strTitle = Peo()->PodObjData()->m_strName;

m_bBackPressed = FALSE;
return CPropertyPage::OnSetActive();

}  //*** CBasePropertyPage::OnSetActive()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::OnApply
//
//Routine Description:
//Handler for the PSM_APPLY message.
//
//Arguments:
//None.
//
//Return Value:
//TRUEPage successfully applied.
//FALSEError applying page.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CBasePropertyPage::OnApply(void)
{
ASSERT(!BWizard());

AFX_MANAGE_STATE(AfxGetStaticModuleState());

CWaitCursorwc;

// Update the data in the class from the page.
if (!UpdateData(TRUE /*bSaveAndValidate*/))
return FALSE;

if (!BApplyChanges())
return FALSE;

return CPropertyPage::OnApply();

}  //*** CBasePropertyPage::OnApply()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::OnWizardBack
//
//Routine Description:
//Handler for the PSN_WIZBACK message.
//
//Arguments:
//None.
//
//Return Value:
//-1Don't change the page.
//0Change the page.
//
//--
/////////////////////////////////////////////////////////////////////////////
LRESULT CBasePropertyPage::OnWizardBack(void)
{
LRESULTlResult;

ASSERT(BWizard());

lResult = CPropertyPage::OnWizardBack();
if (lResult != -1)
m_bBackPressed = TRUE;

return lResult;

}  //*** CBasePropertyPage::OnWizardBack()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::OnWizardNext
//
//Routine Description:
//Handler for the PSN_WIZNEXT message.
//
//Arguments:
//None.
//
//Return Value:
//-1Don't change the page.
//0Change the page.
//
//--
/////////////////////////////////////////////////////////////////////////////
LRESULT CBasePropertyPage::OnWizardNext(void)
{
ASSERT(BWizard());

AFX_MANAGE_STATE(AfxGetStaticModuleState());

CWaitCursorwc;

// Update the data in the class from the page.
if (!UpdateData(TRUE /*bSaveAndValidate*/))
return -1;

// Save the data in the sheet.
if (!BApplyChanges())
return -1;

// Create the object.

return CPropertyPage::OnWizardNext();

}  //*** CBasePropertyPage::OnWizardNext()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::OnWizardFinish
//
//Routine Description:
//Handler for the PSN_WIZFINISH message.
//
//Arguments:
//None.
//
//Return Value:
//FALSEDon't change the page.
//TRUEChange the page.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CBasePropertyPage::OnWizardFinish(void)
{
ASSERT(BWizard());

AFX_MANAGE_STATE(AfxGetStaticModuleState());

CWaitCursorwc;

// Update the data in the class from the page.
if (!UpdateData(TRUE /*bSaveAndValidate*/))
return FALSE;

// Save the data in the sheet.
if (!BApplyChanges())
return FALSE;

return CPropertyPage::OnWizardFinish();

}  //*** CBasePropertyPage::OnWizardFinish()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::OnChangeCtrl
//
//Routine Description:
//Handler for the messages sent when a control is changed.  This
//method can be specified in a message map if all that needs to be
//done is enable the Apply button.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CBasePropertyPage::OnChangeCtrl(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

SetModified(TRUE);

}  //*** CBasePropertyPage::OnChangeCtrl()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::EnableNext
//
//Routine Description:
//Enables or disables the NEXT or FINISH button.
//
//Arguments:
//bEnable[IN] TRUE = enable the button, FALSE = disable the button.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CBasePropertyPage::EnableNext(IN BOOL bEnable /*TRUE*/)
{
ASSERT(BWizard());

AFX_MANAGE_STATE(AfxGetStaticModuleState());

PiWizardCallback()->EnableNext((LONG *) Hpage(), bEnable);

}  //*** CBasePropertyPage::EnableNext()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::BApplyChanges
//
//Routine Description:
//Apply changes made on the page.
//
//Arguments:
//None.
//
//Return Value:
//TRUEPage successfully applied.
//FALSEError applying page.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CBasePropertyPage::BApplyChanges(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

CWaitCursorwc;

// Save data.
return BSetPrivateProps();

}  //*** CBasePropertyPage::BApplyChanges()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::BuildPropList
//
//Routine Description:
//Build the property list.
//
//Arguments:
//rcpl[IN OUT] Cluster property list.
//
//Return Value:
//None.
//
//Exceptions Thrown:
//Any exceptions thrown by CClusPropList::AddProp().
//
//--
/////////////////////////////////////////////////////////////////////////////
void CBasePropertyPage::BuildPropList(
IN OUT CClusPropList & rcpl
)
{
DWORDcprop;
const CObjectProperty *pprop;

for (pprop = Pprops(), cprop = Cprops() ; cprop > 0 ; pprop++, cprop--)
{
switch (pprop->m_propFormat)
{
case CLUSPROP_FORMAT_SZ:
case CLUSPROP_FORMAT_EXPAND_SZ:
rcpl.AddProp(
pprop->m_pwszName,
*pprop->m_value.pstr,
*pprop->m_valuePrev.pstr
);
break;
case CLUSPROP_FORMAT_DWORD:
rcpl.AddProp(
pprop->m_pwszName,
*pprop->m_value.pdw,
*pprop->m_valuePrev.pdw
);
break;
case CLUSPROP_FORMAT_BINARY:
case CLUSPROP_FORMAT_MULTI_SZ:
rcpl.AddProp(
pprop->m_pwszName,
*pprop->m_value.ppb,
*pprop->m_value.pcb,
*pprop->m_valuePrev.ppb,
*pprop->m_valuePrev.pcb
);
break;
default:
ASSERT(0);// don't know how to deal with this type
return;
}  // switch:  property format
}  // for:  each property

}  //*** CBasePropertyPage::BuildPropList()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CBasePropertyPage::BSetPrivateProps
//
//Routine Description:
//Set the private properties for this object.
//
//Arguments:
//bValidateOnly[IN] TRUE = only validate the data.
//
//Return Value:
//ERROR_SUCCESSThe operation was completed successfully.
//!0Failure.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CBasePropertyPage::BSetPrivateProps(IN BOOL bValidateOnly)
{
BOOLbSuccess= TRUE;
CClusPropListcpl(BWizard() /*bAlwaysAddProp*/);
CWaitCursorwc;

ASSERT(Peo() != NULL);

// Build the property list.
try
{
BuildPropList(cpl);
}  // try
catch (CException * pe)
{
pe->ReportError();
pe->Delete();
bSuccess = FALSE;
}  // catch:  CException

// Set the data.
if (bSuccess)
{
if ((cpl.PbProplist() != NULL) && (cpl.CbProplist() > 0))
{
DWORDdwStatus;
DWORDdwControlCode;
DWORDcbProps;

switch (Cot())
{
case CLUADMEX_OT_NODE:
ASSERT(Peo()->PndNodeData() != NULL);
ASSERT(Peo()->PndNodeData()->m_hnode != NULL);

// Determine which control code to use.
if (bValidateOnly)
dwControlCode = CLUSCTL_NODE_VALIDATE_PRIVATE_PROPERTIES;
else
dwControlCode = CLUSCTL_NODE_SET_PRIVATE_PROPERTIES;

// Set private properties.
dwStatus = ClusterNodeControl(
Peo()->PndNodeData()->m_hnode,
NULL,// hNode
dwControlCode,
cpl.PbProplist(),
cpl.CbProplist(),
NULL,// lpOutBuffer
0,// nOutBufferSize
&cbProps
);
break;
case CLUADMEX_OT_GROUP:
ASSERT(Peo()->PgdGroupData() != NULL);
ASSERT(Peo()->PgdGroupData()->m_hgroup != NULL);

// Determine which control code to use.
if (bValidateOnly)
dwControlCode = CLUSCTL_GROUP_VALIDATE_PRIVATE_PROPERTIES;
else
dwControlCode = CLUSCTL_GROUP_SET_PRIVATE_PROPERTIES;

// Set private properties.
dwStatus = ClusterGroupControl(
Peo()->PgdGroupData()->m_hgroup,
NULL,// hNode
dwControlCode,
cpl.PbProplist(),
cpl.CbProplist(),
NULL,// lpOutBuffer
0,// nOutBufferSize
&cbProps
);
break;
case CLUADMEX_OT_RESOURCE:
ASSERT(Peo()->PrdResData() != NULL);
ASSERT(Peo()->PrdResData()->m_hresource != NULL);

// Determine which control code to use.
if (bValidateOnly)
dwControlCode = CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES;
else
dwControlCode = CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES;

// Set private properties.
dwStatus = ClusterResourceControl(
Peo()->PrdResData()->m_hresource,
NULL,// hNode
dwControlCode,
cpl.PbProplist(),
cpl.CbProplist(),
NULL,// lpOutBuffer
0,// nOutBufferSize
&cbProps
);
break;
case CLUADMEX_OT_RESOURCETYPE:
ASSERT(Peo()->PodObjData() != NULL);
ASSERT(Peo()->PodObjData()->m_strName.GetLength() > 0);

// Determine which control code to use.
if (bValidateOnly)
dwControlCode = CLUSCTL_RESOURCE_TYPE_VALIDATE_PRIVATE_PROPERTIES;
else
dwControlCode = CLUSCTL_RESOURCE_TYPE_SET_PRIVATE_PROPERTIES;

// Set private properties.
dwStatus = ClusterResourceTypeControl(
Hcluster(),
Peo()->PodObjData()->m_strName,
NULL,// hNode
dwControlCode,
cpl.PbProplist(),
cpl.CbProplist(),
NULL,// lpOutBuffer
0,// nOutBufferSize
&cbProps
);
break;
case CLUADMEX_OT_NETWORK:
ASSERT(Peo()->PndNetworkData() != NULL);
ASSERT(Peo()->PndNetworkData()->m_hnetwork != NULL);

// Determine which control code to use.
if (bValidateOnly)
dwControlCode = CLUSCTL_NETWORK_VALIDATE_PRIVATE_PROPERTIES;
else
dwControlCode = CLUSCTL_NETWORK_SET_PRIVATE_PROPERTIES;

// Set private properties.
dwStatus = ClusterNetworkControl(
Peo()->PndNetworkData()->m_hnetwork,
NULL,// hNode
dwControlCode,
cpl.PbProplist(),
cpl.CbProplist(),
NULL,// lpOutBuffer
0,// nOutBufferSize
&cbProps
);
break;
case CLUADMEX_OT_NETINTERFACE:
ASSERT(Peo()->PndNetInterfaceData() != NULL);
ASSERT(Peo()->PndNetInterfaceData()->m_hnetinterface != NULL);

// Determine which control code to use.
if (bValidateOnly)
dwControlCode = CLUSCTL_NETINTERFACE_VALIDATE_PRIVATE_PROPERTIES;
else
dwControlCode = CLUSCTL_NETINTERFACE_SET_PRIVATE_PROPERTIES;

// Set private properties.
dwStatus = ClusterNetInterfaceControl(
Peo()->PndNetInterfaceData()->m_hnetinterface,
NULL,// hNode
dwControlCode,
cpl.PbProplist(),
cpl.CbProplist(),
NULL,// lpOutBuffer
0,// nOutBufferSize
&cbProps
);
break;
default:
ASSERT(0);
}  // switch:  object type

// Handle errors.
if (dwStatus != ERROR_SUCCESS)
{
CString strMsg;
FormatError(strMsg, dwStatus);
AfxMessageBox(strMsg);
if (bValidateOnly
|| (dwStatus != ERROR_RESOURCE_PROPERTIES_STORED))
bSuccess = FALSE;
}  // if:  error setting/validating data
}  // if:  there is data to set
}  // if:  no errors building the property list

// Save data locally.
if (!bValidateOnly && bSuccess)
{
// Save new values as previous values.
try
{
DWORDcprop;
const CObjectProperty *pprop;

for (pprop = Pprops(), cprop = Cprops() ; cprop > 0 ; pprop++, cprop--)
{
switch (pprop->m_propFormat)
{
case CLUSPROP_FORMAT_SZ:
case CLUSPROP_FORMAT_EXPAND_SZ:
ASSERT(pprop->m_value.pstr != NULL);
ASSERT(pprop->m_valuePrev.pstr != NULL);
*pprop->m_valuePrev.pstr = *pprop->m_value.pstr;
break;
case CLUSPROP_FORMAT_DWORD:
ASSERT(pprop->m_value.pdw != NULL);
ASSERT(pprop->m_valuePrev.pdw != NULL);
*pprop->m_valuePrev.pdw = *pprop->m_value.pdw;
break;
case CLUSPROP_FORMAT_BINARY:
case CLUSPROP_FORMAT_MULTI_SZ:
ASSERT(pprop->m_value.ppb != NULL);
ASSERT(*pprop->m_value.ppb != NULL);
ASSERT(pprop->m_value.pcb != NULL);
ASSERT(pprop->m_valuePrev.ppb != NULL);
ASSERT(*pprop->m_valuePrev.ppb != NULL);
ASSERT(pprop->m_valuePrev.pcb != NULL);
delete [] *pprop->m_valuePrev.ppb;
*pprop->m_valuePrev.ppb = new BYTE[*pprop->m_value.pcb];
CopyMemory(*pprop->m_valuePrev.ppb, *pprop->m_value.ppb, *pprop->m_value.pcb);
*pprop->m_valuePrev.pcb = *pprop->m_value.pcb;
break;
default:
ASSERT(0);// don't know how to deal with this type

}  // switch:  property format
}  // for:  each property
}  // try
catch (CException * pe)
{
pe->ReportError();
pe->Delete();
bSuccess = FALSE;
}  // catch:  CException
}  // if:  not just validating and successful so far

return bSuccess;

}  //*** CBasePropertyPage::BSetPrivateProps()


REGEXT.CPP

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//RegExt.cpp
//
//Abstract:
//Implementation of routines for extension registration.
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#include <stdafx.h>
#include <ole2.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define REG_VALUE_ADMIN_EXTENSIONS L"AdminExtensions"

/////////////////////////////////////////////////////////////////////////////
// Static Function Prototypes
/////////////////////////////////////////////////////////////////////////////

static HRESULT RegisterAnyCluAdminExtension(
IN HCLUSTERhCluster,
IN LPCWSTRpwszKeyName,
IN const CLSID *pClsid
);
static HRESULT RegisterAnyCluAdminExtension(
IN HKEYhkey,
IN const CLSID *pClsid
);
static HRESULT UnregisterAnyCluAdminExtension(
IN HCLUSTERhCluster,
IN LPCWSTRpwszKeyName,
IN const CLSID *pClsid
);
static HRESULT UnregisterAnyCluAdminExtension(
IN HKEYhkey,
IN const CLSID *pClsid
);
static DWORD ReadValue(
IN HKEYhkey,
IN LPCWSTRpwszValueName,
OUT LPWSTR *ppwszValue,
OUT DWORD *pcbSize
);

/////////////////////////////////////////////////////////////////////////////
//++
//
//RegisterCluAdminClusterExtension
//
//Routine Description:
//Register with the cluster database a Cluster Administrator Extension
//DLL that extends the cluster object.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminClusterExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;
HKEYhkey;

// Get the cluster registry key.
hkey = GetClusterKey(hCluster, KEY_ALL_ACCESS);
if (hkey == NULL)
hr = GetLastError();
else
{
// Register the extension.
hr = RegisterAnyCluAdminExtension(hkey, pClsid);

ClusterRegCloseKey(hkey);
}  // else:  GetClusterKey succeeded

return hr;

}  //*** RegisterCluAdminClusterExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//RegisterCluAdminAllNodesExtension
//
//Routine Description:
//Register with the cluster database a Cluster Administrator Extension
//DLL that extends all nodes.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllNodesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = RegisterAnyCluAdminExtension(hCluster, L"Nodes", pClsid);

return hr;

}  //*** RegisterCluAdminAllNodesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//RegisterCluAdminAllGroupsExtension
//
//Routine Description:
//Register with the cluster database a Cluster Administrator Extension
//DLL that extends all groups.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllGroupsExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = RegisterAnyCluAdminExtension(hCluster, L"Groups", pClsid);

return hr;

}  //*** RegisterCluAdminAllGroupsExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//RegisterCluAdminAllResourcesExtension
//
//Routine Description:
//Register with the cluster database a Cluster Administrator Extension
//DLL that extends all resources.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllResourcesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = RegisterAnyCluAdminExtension(hCluster, L"Resources", pClsid);

return hr;

}  //*** RegisterCluAdminAllResourcesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//RegisterCluAdminAllResourceTypesExtension
//
//Routine Description:
//Register with the cluster database a Cluster Administrator Extension
//DLL that extends all resource types.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllResourceTypesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = RegisterAnyCluAdminExtension(hCluster, L"ResourceTypes", pClsid);

return hr;

}  //*** RegisterCluAdminAllResourceTypesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//RegisterCluAdminAllNetworksExtension
//
//Routine Description:
//Register with the cluster database a Cluster Administrator Extension
//DLL that extends all networks.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllNetworksExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = RegisterAnyCluAdminExtension(hCluster, L"Networks", pClsid);

return hr;

}  //*** RegisterCluAdminAllNetworksExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//RegisterCluAdminAllNetInterfacesExtension
//
//Routine Description:
//Register with the cluster database a Cluster Administrator Extension
//DLL that extends all network interfaces.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllNetInterfacesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = RegisterAnyCluAdminExtension(hCluster, L"NetInterfaces", pClsid);

return hr;

}  //*** RegisterCluAdminAllNetInterfacesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//RegisterCluAdminResourceTypeExtension
//
//Routine Description:
//Register with the cluster database a Cluster Administrator Extension
//DLL that extends resources of a specific type, or the resource type
//itself.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pwszResourceType[IN] Resource type name.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminResourceTypeExtension(
IN HCLUSTERhCluster,
IN LPCWSTRpwszResourceType,
IN const CLSID *pClsid
)
{
HRESULThr;
HKEYhkey;

// Get the resource type registry key.
hkey = GetClusterResourceTypeKey(hCluster, pwszResourceType, KEY_ALL_ACCESS);
if (hkey == NULL)
hr = GetLastError();
else
{
// Register the extension.
hr = RegisterAnyCluAdminExtension(hkey, pClsid);

ClusterRegCloseKey(hkey);
}  // else:  GetClusterResourceTypeKey succeeded

return hr;

}  //*** RegisterCluAdminResourceTypeExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//UnregisterCluAdminClusterExtension
//
//Routine Description:
//Unregister with the cluster database a Cluster Administrator Extension
//DLL that extends the cluster object.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminClusterExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;
HKEYhkey;

// Get the cluster registry key.
hkey = GetClusterKey(hCluster, KEY_ALL_ACCESS);
if (hkey == NULL)
hr = GetLastError();
else
{
// Unregister the extension.
hr = UnregisterAnyCluAdminExtension(hkey, pClsid);

ClusterRegCloseKey(hkey);
}  // else:  GetClusterKey succeeded

return hr;

}  //*** UnregisterCluAdminClusterExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//UnregisterCluAdminAllNodesExtension
//
//Routine Description:
//Unregister with the cluster database a Cluster Administrator Extension
//DLL that extends all nodes.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension unregistered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllNodesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = UnregisterAnyCluAdminExtension(hCluster, L"Nodes", pClsid);

return hr;

}  //*** UnregisterCluAdminAllNodesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//UnregisterCluAdminAllGroupsExtension
//
//Routine Description:
//Unregister with the cluster database a Cluster Administrator Extension
//DLL that extends all groups.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension unregistered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllGroupsExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = UnregisterAnyCluAdminExtension(hCluster, L"Groups", pClsid);

return hr;

}  //*** UnregisterCluAdminAllGroupsExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//UnregisterCluAdminAllResourcesExtension
//
//Routine Description:
//Unregister with the cluster database a Cluster Administrator Extension
//DLL that extends all resources.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension unregistered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllResourcesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = UnregisterAnyCluAdminExtension(hCluster, L"Resources", pClsid);

return hr;

}  //*** UnregisterCluAdminAllResourcesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//UnregisterCluAdminAllResourceTypesExtension
//
//Routine Description:
//Unregister with the cluster database a Cluster Administrator Extension
//DLL that extends all resource types.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension unregistered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllResourceTypesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = UnregisterAnyCluAdminExtension(hCluster, L"ResourceTypes", pClsid);

return hr;

}  //*** UnregisterCluAdminAllResourceTypesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//UnregisterCluAdminAllNetworksExtension
//
//Routine Description:
//Unregister with the cluster database a Cluster Administrator Extension
//DLL that extends all networks.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension unregistered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllNetworksExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = UnregisterAnyCluAdminExtension(hCluster, L"Networks", pClsid);

return hr;

}  //*** UnregisterCluAdminAllNetworksExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//UnregisterCluAdminAllNetInterfacesExtension
//
//Routine Description:
//Unregister with the cluster database a Cluster Administrator Extension
//DLL that extends all network interfaces.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension unregistered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllNetInterfacesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
)
{
HRESULThr;

hr = UnregisterAnyCluAdminExtension(hCluster, L"NetInterfaces", pClsid);

return hr;

}  //*** UnregisterCluAdminAllNetInterfacesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//UnregisterCluAdminResourceTypeExtension
//
//Routine Description:
//Unregister with the cluster database a Cluster Administrator Extension
//DLL that extends resources of a specific type, or the resource type
//itself.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pwszResourceType[IN] Resource type name.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension unregistered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminResourceTypeExtension(
IN HCLUSTERhCluster,
IN LPCWSTRpwszResourceType,
IN const CLSID *pClsid
)
{
HRESULThr;
HKEYhkey;

// Get the resource type registry key.
hkey = GetClusterResourceTypeKey(hCluster, pwszResourceType, KEY_ALL_ACCESS);
if (hkey == NULL)
hr = GetLastError();
else
{
// Unregister the extension.
hr = UnregisterAnyCluAdminExtension(hkey, pClsid);

ClusterRegCloseKey(hkey);
}  // else:  GetClusterResourceTypeKey succeeded

return hr;

}  //*** UnregisterCluAdminResourceTypeExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//RegisterAnyCluAdminExtension
//
//Routine Description:
//Register any Cluster Administrator Extension DLL with the cluster
//database.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pwszKeyName[IN] Key name.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
static HRESULT RegisterAnyCluAdminExtension(
IN HCLUSTERhCluster,
IN LPCWSTRpwszKeyName,
IN const CLSID *pClsid
)
{
HRESULThr;
HKEYhkeyCluster;
HKEYhkey;

// Get the cluster key.
hkeyCluster = GetClusterKey(hCluster, KEY_ALL_ACCESS);
if (hkeyCluster == NULL)
hr = GetLastError();
else
{
// Get the specified key.
hr = ClusterRegOpenKey(hkeyCluster, pwszKeyName, KEY_ALL_ACCESS, &hkey);
if (hr == ERROR_SUCCESS)
{
// Register the extension.
hr = RegisterAnyCluAdminExtension(hkey, pClsid);

ClusterRegCloseKey(hkey);
}  // else:  GetClusterResourceTypeKey succeeded

ClusterRegCloseKey(hkeyCluster);
}  // if:  CLSID converted to a string successfully

return hr;

}  //*** RegisterAnyCluAdminExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//RegisterAnyCluAdminExtension
//
//Routine Description:
//Register any Cluster Administrator Extension DLL with the cluster
//database.
//
//Arguments:
//hkey[IN] Cluster database key.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
static HRESULT RegisterAnyCluAdminExtension(
IN HKEYhkey,
IN const CLSID *pClsid
)
{
HRESULThr;
LPOLESTRpwszClsid;
DWORDcbSize;
DWORDcbNewSize;
LPWSTRpwszValue;
LPWSTRpwszNewValue;
BOOLbAlreadyRegistered;

// Convert the CLSID to a string.
hr = StringFromCLSID(*pClsid, &pwszClsid);
if (hr == S_OK)
{
// Read the current value.
hr = ReadValue(hkey, REG_VALUE_ADMIN_EXTENSIONS, &pwszValue, &cbSize);
if (hr == S_OK)
{
// Check to see if the extension has been registered yet.
if (pwszValue == NULL)
bAlreadyRegistered = FALSE;
else
{
LPCWSTRpwszValueBuf = pwszValue;

while (*pwszValueBuf != L'\0')
{
if (lstrcmpiW(pwszClsid, pwszValueBuf) == 0)
break;
pwszValueBuf += lstrlenW(pwszValueBuf) + 1;
}  // while:  more strings in the extension list
bAlreadyRegistered = (*pwszValueBuf != L'\0');
}  // else:  extension value exists

// Register the extension.
if (!bAlreadyRegistered)
{
// Allocate a new buffer.
cbNewSize = cbSize + (lstrlenW(pwszClsid) + 1) * sizeof(WCHAR);
if (cbSize == 0) // Add size of final NULL if first entry.
cbNewSize += sizeof(WCHAR);
pwszNewValue = (LPWSTR) LocalAlloc(LMEM_FIXED, cbNewSize);
if (pwszNewValue == NULL)
hr = GetLastError();
else
{
LPCWSTRpwszValueBuf= pwszValue;
LPWSTRpwszNewValueBuf= pwszNewValue;
DWORDcch;
DWORDdwType;

// Copy the existing extensions to the new buffer.
if (pwszValue != NULL)
{
while (*pwszValueBuf != L'\0')
{
lstrcpyW(pwszNewValueBuf, pwszValueBuf);
cch = lstrlenW(pwszValueBuf);
pwszValueBuf += cch + 1;
pwszNewValueBuf += cch + 1;
}  // while:  more strings in the extension list
}  // if:  previous value buffer existed

// Add the new CLSID to the list.
lstrcpyW(pwszNewValueBuf, pwszClsid);
pwszNewValueBuf += lstrlenW(pwszClsid) + 1;
*pwszNewValueBuf = L'\0';

// Write the value to the cluster database.
dwType = REG_MULTI_SZ;
hr = ClusterRegSetValue(
hkey,
REG_VALUE_ADMIN_EXTENSIONS,
dwType,
(LPBYTE) pwszNewValue,
cbNewSize
);

LocalFree(pwszNewValue);
}  // else:  new buffer allocated successfully

}  // if:  extension not registered yet

LocalFree(pwszValue);
}  // if:  value read successfully

CoTaskMemFree(pwszClsid);
}  // if:  CLSID converted to a string successfully

return hr;

}  //*** RegisterAnyCluAdminExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//UnregisterAnyCluAdminExtension
//
//Routine Description:
//Unregister any Cluster Administrator Extension DLL with the cluster
//database.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//pwszKeyName[IN] Key name.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension unregistered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
static HRESULT UnregisterAnyCluAdminExtension(
IN HCLUSTERhCluster,
IN LPCWSTRpwszKeyName,
IN const CLSID *pClsid
)
{
HRESULThr;
HKEYhkeyCluster;
HKEYhkey;

// Get the cluster key.
hkeyCluster = GetClusterKey(hCluster, KEY_ALL_ACCESS);
if (hkeyCluster == NULL)
hr = GetLastError();
else
{
// Get the specified key.
hr = ClusterRegOpenKey(hkeyCluster, pwszKeyName, KEY_ALL_ACCESS, &hkey);
if (hr == ERROR_SUCCESS)
{
// Unregister the extension.
hr = UnregisterAnyCluAdminExtension(hkey, pClsid);

ClusterRegCloseKey(hkey);
}  // else:  GetClusterResourceTypeKey succeeded

ClusterRegCloseKey(hkeyCluster);
}  // if:  CLSID converted to a string successfully

return hr;

}  //*** UnregisterAnyCluAdminExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//UnregisterAnyCluAdminExtension
//
//Routine Description:
//Unregister any Cluster Administrator Extension DLL with the cluster
//database.
//
//Arguments:
//hkey[IN] Cluster database key.
//pClsid[IN] Extension's CLSID.
//
//Return Value:
//S_OKExtension unregistered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
static HRESULT UnregisterAnyCluAdminExtension(
IN HKEYhkey,
IN const CLSID *pClsid
)
{
HRESULThr;
LPOLESTRpwszClsid;
DWORDcbSize;
DWORDcbNewSize;
LPWSTRpwszValue;
LPWSTRpwszNewValue;
BOOLbAlreadyUnregistered;

// Convert the CLSID to a string.
hr = StringFromCLSID(*pClsid, &pwszClsid);
if (hr == S_OK)
{
// Read the current value.
hr = ReadValue(hkey, REG_VALUE_ADMIN_EXTENSIONS, &pwszValue, &cbSize);
if (hr == S_OK)
{
// Check to see if the extension has been unregistered yet.
if (pwszValue == NULL)
bAlreadyUnregistered = TRUE;
else
{
LPCWSTR pwszValueBuf = pwszValue;

while (*pwszValueBuf != L'\0')
{
if (lstrcmpiW(pwszClsid, pwszValueBuf) == 0)
break;
pwszValueBuf += lstrlenW(pwszValueBuf) + 1;
}  // while:  more strings in the extension list
bAlreadyUnregistered = (*pwszValueBuf == L'\0');
}  // else:  extension value exists

// Unregister the extension.
if (!bAlreadyUnregistered)
{
// Allocate a new buffer.
cbNewSize = cbSize - (lstrlenW(pwszClsid) + 1) * sizeof(WCHAR);
if (cbNewSize == sizeof(WCHAR))
cbNewSize = 0;
pwszNewValue = (LPWSTR) LocalAlloc(LMEM_FIXED, cbNewSize);
if (pwszNewValue == NULL)
hr = GetLastError();
else
{
LPCWSTRpwszValueBuf= pwszValue;
LPWSTRpwszNewValueBuf= pwszNewValue;
DWORDdwType;

// Copy the existing extensions to the new buffer.
if ((cbNewSize > 0) && (pwszValue != NULL))
{
while (*pwszValueBuf != L'\0')
{
if (lstrcmpiW(pwszClsid, pwszValueBuf) != 0)
{
lstrcpyW(pwszNewValueBuf, pwszValueBuf);
pwszNewValueBuf += lstrlenW(pwszNewValueBuf) + 1;
}  // if:  not CLSID being removed
pwszValueBuf += lstrlenW(pwszValueBuf) + 1;
}  // while:  more strings in the extension list
*pwszNewValueBuf = L'\0';
}  // if:  previous value buffer existed

// Write the value to the cluster database.
dwType = REG_MULTI_SZ;
hr = ClusterRegSetValue(
hkey,
REG_VALUE_ADMIN_EXTENSIONS,
dwType,
(LPBYTE) pwszNewValue,
cbNewSize
);

LocalFree(pwszNewValue);
}  // else:  new buffer allocated successfully

}  // if:  extension not unregistered yet

LocalFree(pwszValue);
}  // if:  value read successfully

CoTaskMemFree(pwszClsid);
}  // if:  CLSID converted to a string successfully

return hr;

}  //*** UnregisterAnyCluAdminExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//ReadValue
//
//Routine Description:
//Reads a value from the cluster database.
//
//Arguments:
//hkey[IN] Handle for the key to read from.
//pwszValueName[IN] Name of value to read.
//ppwszValue[OUT] Address of pointer in which to return data.
//The string is allocated using LocalAlloc and must
//be deallocated by the calling LocalFree.
//pcbSize[OUT] Size in bytes of the allocated value buffer.
//
//Return Value:
//Any return values from ClusterRegQueryValue or errors from new.
//
//--
/////////////////////////////////////////////////////////////////////////////

static DWORD ReadValue(
IN HKEYhkey,
IN LPCWSTRpwszValueName,
OUT LPWSTR *ppwszValue,
OUT DWORD *pcbSize
)
{
DWORDdwStatus;
DWORDcbSize;
DWORDdwType;
LPWSTRpwszValue;

*ppwszValue = NULL;
*pcbSize = 0;

// Get the length of the value.
dwStatus = ClusterRegQueryValue(
hkey,
pwszValueName,
&dwType,
NULL,
&cbSize
);
if (   (dwStatus != ERROR_SUCCESS)
&& (dwStatus != ERROR_MORE_DATA))
{
if (dwStatus  == ERROR_FILE_NOT_FOUND)
dwStatus = ERROR_SUCCESS;
return dwStatus;
}  // if:  error occurred

if (cbSize > 0)
{
// Allocate a value string.
pwszValue = (LPWSTR) LocalAlloc(LMEM_FIXED, cbSize);
if (pwszValue == NULL)
{
dwStatus = GetLastError();
return dwStatus;
}  // if:  error allocating memory

// Read the the value.
dwStatus = ClusterRegQueryValue(
hkey,
pwszValueName,
&dwType,
(LPBYTE) pwszValue,
&cbSize
);
if (dwStatus != ERROR_SUCCESS)
{
LocalFree(pwszValue);
pwszValue = NULL;
cbSize = 0;
}  // if:  error occurred

*ppwszValue = pwszValue;
*pcbSize = cbSize;
}  // if:  value is not empty

return dwStatus;

}  //*** ReadValue()


REGEXT.H

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//RegExt.h
//
//Abstract:
//Definitions of routines for extension registration.
//
//Implementation File:
//RegExt.cpp
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#ifndef _REGEXT_H_
#define _REGEXT_H_

/////////////////////////////////////////////////////////////////////////////
// Global Function Declarations
/////////////////////////////////////////////////////////////////////////////

// Registration routines.

STDAPI RegisterCluAdminClusterExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI RegisterCluAdminAllNodesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI RegisterCluAdminAllGroupsExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI RegisterCluAdminAllResourcesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI RegisterCluAdminAllResourceTypesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI RegisterCluAdminAllNetworksExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI RegisterCluAdminAllNetInterfacesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI RegisterCluAdminResourceTypeExtension(
IN HCLUSTERhCluster,
IN LPCWSTRpwszResourceType,
IN const CLSID *pClsid
);

// Unregistration routines.

STDAPI UnregisterCluAdminClusterExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI UnregisterCluAdminAllNodesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI UnregisterCluAdminAllGroupsExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI UnregisterCluAdminAllResourcesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI UnregisterCluAdminAllResourceTypesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI UnregisterCluAdminAllNetworksExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI UnregisterCluAdminAllNetInterfacesExtension(
IN HCLUSTERhCluster,
IN const CLSID *pClsid
);

STDAPI UnregisterCluAdminResourceTypeExtension(
IN HCLUSTERhCluster,
IN LPCWSTRpwszResourceType,
IN const CLSID *pClsid
);

/////////////////////////////////////////////////////////////////////////////

#endif // _REGEXT_H_


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by SmbSmp.rc
//
#define IDD_PP_SMBSMP_PARAMETERS      130
#define IDD_WIZ_SMBSMP_PARAMETERS     131
#define IDC_PP_ICON                     1000
#define IDC_PP_TITLE                    1001
#define IDC_PP_SMBSMP_SHARENAME_LABEL 1010
#define IDC_PP_SMBSMP_SHARENAME       1011
#define IDC_PP_SMBSMP_PATH_LABEL 1012
#define IDC_PP_SMBSMP_PATH       1013
#define IDC_PP_SMBSMP_REMARK_LABEL 1014
#define IDC_PP_SMBSMP_REMARK       1015
#define IDS_CLUADMEX_COMOBJ_DESC        31100
#define IDS_REQUIRED_FIELD_EMPTY        31101
#define IDS_APPLY_PARAM_CHANGES_ERROR   31200

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        134
#define _APS_NEXT_COMMAND_VALUE         32771
#define _APS_NEXT_CONTROL_VALUE         1110
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


RESPROP.CPP

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//ResProp.cpp
//
//Abstract:
//Implementation of the resource extension property page classes.
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SmbSmpEx.h"
#include "ResProp.h"
#include "ExtObj.h"
#include "DDxDDv.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CSmbSmpParamsPage property page
/////////////////////////////////////////////////////////////////////////////

IMPLEMENT_DYNCREATE(CSmbSmpParamsPage, CBasePropertyPage)

/////////////////////////////////////////////////////////////////////////////
// Message Maps

BEGIN_MESSAGE_MAP(CSmbSmpParamsPage, CBasePropertyPage)
//{{AFX_MSG_MAP(CSmbSmpParamsPage)
ON_EN_CHANGE(IDC_PP_SMBSMP_SHARENAME, OnChangeRequiredField)
ON_EN_CHANGE(IDC_PP_SMBSMP_PATH, OnChangeRequiredField)
//}}AFX_MSG_MAP
// TODO: Modify the following lines to represent the data displayed on this page.
ON_EN_CHANGE(IDC_PP_SMBSMP_SHARENAME, CBasePropertyPage::OnChangeCtrl)
ON_EN_CHANGE(IDC_PP_SMBSMP_PATH, CBasePropertyPage::OnChangeCtrl)
ON_EN_CHANGE(IDC_PP_SMBSMP_REMARK, CBasePropertyPage::OnChangeCtrl)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CSmbSmpParamsPage::CSmbSmpParamsPage
//
//Routine Description:
//Default constructor.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CSmbSmpParamsPage::CSmbSmpParamsPage(void) : CBasePropertyPage(CSmbSmpParamsPage::IDD)
{
// TODO: Modify the following lines to represent the data displayed on this page.
//{{AFX_DATA_INIT(CSmbSmpParamsPage)
m_strShareName = _T("");
m_strPath = _T("");
m_strRemark = _T("");
//}}AFX_DATA_INIT

// Setup the property array.
{
m_rgProps[epropShareName].Set(REGPARAM_SMBSMP_SHARENAME, m_strShareName, m_strPrevShareName);
m_rgProps[epropPath].Set(REGPARAM_SMBSMP_PATH, m_strPath, m_strPrevPath);
m_rgProps[epropRemark].Set(REGPARAM_SMBSMP_REMARK, m_strRemark, m_strPrevRemark);
}  // Setup the property array

m_iddPropertyPage = IDD_PP_SMBSMP_PARAMETERS;
m_iddWizardPage = IDD_WIZ_SMBSMP_PARAMETERS;

}  //*** CSmbSmpParamsPage::CSmbSmpParamsPage()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CSmbSmpParamsPage::DoDataExchange
//
//Routine Description:
//Do data exchange between the dialog and the class.
//
//Arguments:
//pDX[IN OUT] Data exchange object
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CSmbSmpParamsPage::DoDataExchange(CDataExchange * pDX)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

// TODO: Modify the following lines to represent the data displayed on this page.
//{{AFX_DATA_MAP(CSmbSmpParamsPage)
DDX_Control(pDX, IDC_PP_SMBSMP_SHARENAME, m_editShareName);
DDX_Control(pDX, IDC_PP_SMBSMP_PATH, m_editPath);
DDX_Text(pDX, IDC_PP_SMBSMP_SHARENAME, m_strShareName);
DDX_Text(pDX, IDC_PP_SMBSMP_PATH, m_strPath);
DDX_Text(pDX, IDC_PP_SMBSMP_REMARK, m_strRemark);
//}}AFX_DATA_MAP

// Handle numeric parameters.
// TODO: Change the last argument to TRUE if any of these are signed values.
if (!BBackPressed())
{
}  // if:  back button not pressed

// TODO: Add any additional field validation here.
if (pDX->m_bSaveAndValidate)
{
// Make sure all required fields are present.
if (!BBackPressed())
{
DDV_RequiredText(pDX, IDC_PP_SMBSMP_SHARENAME, IDC_PP_SMBSMP_SHARENAME_LABEL, m_strShareName);
DDV_RequiredText(pDX, IDC_PP_SMBSMP_PATH, IDC_PP_SMBSMP_PATH_LABEL, m_strPath);
}  // if:  back button not pressed
}  // if:  saving data from dialog

CBasePropertyPage::DoDataExchange(pDX);

}  //*** CSmbSmpParamsPage::DoDataExchange()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CSmbSmpParamsPage::OnInitDialog
//
//Routine Description:
//Handler for the WM_INITDIALOG message.
//
//Arguments:
//None.
//
//Return Value:
//TRUEWe need the focus to be set for us.
//FALSEWe already set the focus to the proper control.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CSmbSmpParamsPage::OnInitDialog(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

CBasePropertyPage::OnInitDialog();

// TODO:
// Limit the size of the text that can be entered in edit controls.

return TRUE;// return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE

}  //*** CSmbSmpParamsPage::OnInitDialog()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CSmbSmpParamsPage::OnSetActive
//
//Routine Description:
//Handler for the PSN_SETACTIVE message.
//
//Arguments:
//None.
//
//Return Value:
//TRUEPage successfully initialized.
//FALSEPage not initialized.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CSmbSmpParamsPage::OnSetActive(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

// Enable/disable the Next/Finish button.
if (BWizard())
EnableNext(BAllRequiredFieldsPresent());

return CBasePropertyPage::OnSetActive();

}  //*** CSmbSmpParamsPage::OnSetActive()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CSmbSmpParamsPage::OnChangeRequiredField
//
//Routine Description:
//Handler for the EN_CHANGE message on the Share name or Path edit
//controls.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CSmbSmpParamsPage::OnChangeRequiredField(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

OnChangeCtrl();

if (BWizard())
EnableNext(BAllRequiredFieldsPresent());

}  //*** CSmbSmpParamsPage::OnChangeRequiredField()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CSmbSmpParamsPage::BAllRequiredFieldsPresent
//
//Routine Description:
//Handler for the EN_CHANGE message on the Share name or Path edit
//controls.
//
//Arguments:
//None.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CSmbSmpParamsPage::BAllRequiredFieldsPresent(void) const
{
BOOLbPresent;

if (0
|| (m_editShareName.GetWindowTextLength() == 0)
|| (m_editPath.GetWindowTextLength() == 0)
)
bPresent = FALSE;
else
bPresent = TRUE;

return bPresent;

}  //*** CSmbSmpParamsPage::BAllRequiredFieldsPresent()


RESPROP.H

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//ResProp.h
//
//Abstract:
//Definition of the resource extension property page classes.
//
//Implementation File:
//ResProp.cpp
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#ifndef _RESPROP_H_
#define _RESPROP_H_

/////////////////////////////////////////////////////////////////////////////
// Include Files
/////////////////////////////////////////////////////////////////////////////

#ifndef _BASEPAGE_H_
#include "BasePage.h"// for CBasePropertyPage
#endif

#ifndef _PROPLIST_H_
#include "PropList.h"// for CObjectPropert
#endif

/////////////////////////////////////////////////////////////////////////////
// Forward Class Declarations
/////////////////////////////////////////////////////////////////////////////

class CSmbSmpParamsPage;

/////////////////////////////////////////////////////////////////////////////
// External Class Declarations
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
//
//CSmbSmpParamsPage
//
//Purpose:
//Parameters page for resources.
//
/////////////////////////////////////////////////////////////////////////////

class CSmbSmpParamsPage : public CBasePropertyPage
{
DECLARE_DYNCREATE(CSmbSmpParamsPage)

// Construction
public:
CSmbSmpParamsPage(void);

// Dialog Data
//{{AFX_DATA(CSmbSmpParamsPage)
enum { IDD = IDD_PP_SMBSMP_PARAMETERS };
CEditm_editShareName;
CEditm_editPath;
CStringm_strShareName;
CStringm_strPath;
CStringm_strRemark;
//}}AFX_DATA
CStringm_strPrevShareName;
CStringm_strPrevPath;
CStringm_strPrevRemark;

protected:
enum
{
epropShareName,
epropPath,
epropRemark,
epropMAX
};
CObjectPropertym_rgProps[epropMAX];

// Overrides
// ClassWizard generate virtual function overrides
//{{AFX_VIRTUAL(CSmbSmpParamsPage)
public:
virtual BOOL OnSetActive();
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

protected:
virtual const CObjectProperty *Pprops(void) const{ return m_rgProps; }
virtual DWORDCprops(void) const{ return sizeof(m_rgProps) / sizeof(CObjectProperty); }

// Implementation
protected:
BOOLBAllRequiredFieldsPresent(void) const;

// Generated message map functions
//{{AFX_MSG(CSmbSmpParamsPage)
virtual BOOL OnInitDialog();
afx_msg void OnChangeRequiredField();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

};  //*** class CSmbSmpParamsPage

/////////////////////////////////////////////////////////////////////////////

#endif // _RESPROP_H_


SMBSMPEX.CPP

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//SmbSmpEx.cpp
//
//Abstract:
//Implementation of the CSmbSmpApp class and DLL initialization
//routines.
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//NOTE: You must use the MIDL compiler from NT 4.0,
//version 3.00.44 or greater
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <initguid.h>
#include <CluAdmEx.h>
#include "SmbSmpEx.h"
#include "ExtObj.h"
#include "BasePage.h"
#include "RegExt.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define IID_DEFINED
#include "ExtObjID_i.c"

CComModule _Module;

#pragma warning(disable : 4701) // local variable may be used without having been initialized
#include <atlimpl.cpp>
#pragma warning(default : 4701)

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_CoSmbSmpEx, CExtObject)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// Global Function Prototypes
/////////////////////////////////////////////////////////////////////////////

STDAPI DllCanUnloadNow(void);
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
STDAPI DllRegisterServer(void);
STDAPI DllUnregisterServer(void);
STDAPI DllRegisterCluAdminExtension(IN HCLUSTER hcluster);
STDAPI DllUnregisterCluAdminExtension(IN HCLUSTER hcluster);

/////////////////////////////////////////////////////////////////////////////
// class CSmbSmpApp
/////////////////////////////////////////////////////////////////////////////

class CSmbSmpApp : public CWinApp
{
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
};

/////////////////////////////////////////////////////////////////////////////
// The one and only CSmbSmpApp object

CSmbSmpApp theApp;

/////////////////////////////////////////////////////////////////////////////
//++
//
//CSmbSmpApp::InitInstance
//
//Routine Description:
//Initialize this instance of the application.
//
//Arguments:
//None.
//
//Return Value:
//Any return codes from CWinApp::InitInstance().
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CSmbSmpApp::InitInstance(void)
{
_Module.Init(ObjectMap, m_hInstance);
return CWinApp::InitInstance();

}  //*** CSmbSmpApp::InitInstance()

/////////////////////////////////////////////////////////////////////////////
//++
//
//CSmbSmpApp::ExitInstance
//
//Routine Description:
//Deinitialize this instance of the application.
//
//Arguments:
//None.
//
//Return Value:
//Any return codes from CWinApp::ExitInstance().
//
//--
/////////////////////////////////////////////////////////////////////////////
int CSmbSmpApp::ExitInstance(void)
{
_Module.Term();
return CWinApp::ExitInstance();

}  //*** CSmbSmpApp::ExitInstance()

/////////////////////////////////////////////////////////////////////////////
//++
//
//FormatError
//
//Routine Description:
//Format an error.
//
//Arguments:
//rstrError[OUT] String in which to return the error message.
//dwError[IN] Error code to format.
//
//Return Value:
//None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void FormatError(CString & rstrError, DWORD dwError)
{
DWORDdwResult;
TCHARszError[256];

dwResult = ::FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
szError,
sizeof(szError) / sizeof(TCHAR),
0
);
if (dwResult == 0)
{
// Format the NT status code from CLUSAPI since some of these haven't
// been integrated into the system yet.
dwResult = ::FormatMessage(
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
::GetModuleHandle(_T("CLUSAPI.DLL")),
dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
szError,
sizeof(szError) / sizeof(TCHAR),
0
);
if (dwResult == 0)
{
// Format the NT status code from NTDLL since this hasn't been
// integrated into the system yet.
dwResult = ::FormatMessage(
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
::GetModuleHandle(_T("NTDLL.DLL")),
dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
szError,
sizeof(szError) / sizeof(TCHAR),
0
);
}  // if:  error formatting status code from CLUSAPI
}  // if:  error formatting status code from system

if (dwResult != 0)
rstrError = szError;
else
{
dwResult = ::GetLastError();
TRACE(_T("FormatError() - Error 0x%08.8x formatting string for error code 0x%08.8x\n"), dwResult, dwError);
rstrError.Format(_T("Error 0x%08.8x"));
}  // else:  error formatting the message

}  //*** FormatError()

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return (AfxDllCanUnloadNow() && _Module.GetLockCount()==0) ? S_OK : S_FALSE;

}  //*** DllCanUnloadNow()

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);

}  //*** DllGetClassObject()

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
HRESULT hRes = S_OK;
// registers object, typelib and all interfaces in typelib
hRes = _Module.RegisterServer(FALSE /*bRegTypeLib*/);
return hRes;

}  //*** DllRegisterServer()

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
HRESULT hRes = S_OK;
_Module.UnregisterServer();
return hRes;

}  //*** DllUnregisterServer()

/////////////////////////////////////////////////////////////////////////////
//++
//
//DllRegisterCluAdminExtension
//
//Routine Description:
//Register the extension with the cluster database.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//
//Return Value:
//S_OKExtension registered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI DllRegisterCluAdminExtension(IN HCLUSTER hCluster)
{
HRESULThr = S_OK;
LPCWSTRpwszResTypes = g_wszResourceTypeNames;

while (*pwszResTypes != L'\0')
{
wprintf(L"  %s\n", pwszResTypes);
hr = RegisterCluAdminResourceTypeExtension(
hCluster,
pwszResTypes,
&CLSID_CoSmbSmpEx
);
if (hr != S_OK)
break;
pwszResTypes += lstrlenW(pwszResTypes) + 1;
}  // while:  more resource types

return hr;

}  //*** DllRegisterCluAdminExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//DllUnregisterCluAdminExtension
//
//Routine Description:
//Unregister the extension with the cluster database.
//
//Arguments:
//hCluster[IN] Handle to the cluster to modify.
//
//Return Value:
//S_OKExtension unregistered successfully.
//Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI DllUnregisterCluAdminExtension(IN HCLUSTER hCluster)
{
HRESULThr = S_OK;
LPCWSTRpwszResTypes = g_wszResourceTypeNames;

while (*pwszResTypes != L'\0')
{
wprintf(L"  %s\n", pwszResTypes);
hr = UnregisterCluAdminResourceTypeExtension(
hCluster,
pwszResTypes,
&CLSID_CoSmbSmpEx
);
if (hr != S_OK)
break;
pwszResTypes += lstrlenW(pwszResTypes) + 1;
}  // while:  more resource types

return hr;

}  //*** DllUnregisterCluAdminExtension()


SMBSMPEX.H

/////////////////////////////////////////////////////////////////////////////
//
//Copyright (c) 1997 <company name>
//
//Module Name:
//SmbSmpEx.h
//
//Abstract:
//Global definitions across the DLL.
//
//Implementation File:
//SmbSmpEx.cpp
//
//Author:
//<name> (<e-mail name>) Mmmm DD, 1997
//
//Revision History:
//
//Notes:
//
/////////////////////////////////////////////////////////////////////////////

#ifndef _SMBSMPEX_H_
#define _SMBSMPEX_H_

/////////////////////////////////////////////////////////////////////////////
// Include Files
/////////////////////////////////////////////////////////////////////////////

#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif

#include "resource.h"// main symbols

/////////////////////////////////////////////////////////////////////////////
// Constant Definitions
/////////////////////////////////////////////////////////////////////////////

#define REGPARAM_SMBSMP_SHARENAME L"ShareName"
#define REGPARAM_SMBSMP_PATH L"Path"
#define REGPARAM_SMBSMP_REMARK L"Remark"

/////////////////////////////////////////////////////////////////////////////
// Global Function Declarations
/////////////////////////////////////////////////////////////////////////////

void FormatError(CString & rstrError, DWORD dwError);

// Defined in Extensn.cpp
extern const WCHAR g_wszResourceTypeNames[];
extern const DWORD g_cchResourceTypeNames;

/////////////////////////////////////////////////////////////////////////////

#endif // _SMBSMPEX_H_


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//SmbSmpEx.pch will be the pre-compiled header
//stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"


STDAFX.H

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#define VC_EXTRALEAN// Exclude rarely-used stuff from Windows headers

#ifndef _UNICODE
#define _UNICODE// Microsoft Windows NT Cluster Administrator
//   Extension DLLs need to be Unicode
//   applications.
#endif

// Choose which threading model you want by commenting or uncommenting
// the proper constant definition.  If you want multi-threading
// (i.e. "both"), comment both definitions out.  Also change the
// THREADFLAGS_xxx set in the DECLARE_REGISTRY macro invokation in ExtObj.h
//#define _ATL_SINGLE_THREADED
#define _ATL_APARTMENT_THREADED

// Link against the Microsoft Windows NT Cluster API library.
#pragma comment(lib, "clusapi.lib")

// Link against the Cluster Administrator Extensions library.
#pragma comment(lib, "cluadmex.lib")

// Link against Resource Utilities library.
#pragma comment(lib, "resutils.lib")

/////////////////////////////////////////////////////////////////////////////
// Common Pragmas
/////////////////////////////////////////////////////////////////////////////

#pragma warning(disable : 4100)// unreferenced formal parameters
#pragma warning(disable : 4702)// unreachable code
#pragma warning(disable : 4711)// function selected for automatic inline expansion

/////////////////////////////////////////////////////////////////////////////
// Include Files
/////////////////////////////////////////////////////////////////////////////

#include <afxwin.h>// MFC core and standard components
#include <afxext.h>// MFC extensions
#include <afxdisp.h>
#include <afxtempl.h>
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>// MFC support for Windows 95 Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>

#include <clusapi.h>

/////////////////////////////////////////////////////////////////////////////
// Common Types
/////////////////////////////////////////////////////////////////////////////

typedef UINTIDS;
typedef UINTIDD;


README.TXT

Debug Event Browser

The DEB sample demonstrates the Win32 debug functions. This sample is a
simple debugger that can debug both a process that it starts or a currently
running process.

DEB is not a debugger in the traditional sense; it is a browser, as its name
implies. DEB displays the debug events, and their relevant properties, as
they occur and invokes the default handlers supplied either by the debuggee
or the system. Only minimal debug event handling is imposed, so that the
debug events are displayed and the debuggee continues on its normal course
of execution.

The sample is source code compatible for the Intel 80x86, the MIPS R4x00,
and the DEC Alpha AXP Windows NT platforms.


DEB.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#ifndef DEB_H

#define DEB_H

 //-- menu ids
 #define IDM_FILE_OPEN               1101
 #define IDM_FILE_ATTACH             1102
 #define IDM_FILE_EXIT               1109

 #define IDM_EDIT_CUT                1201
 #define IDM_EDIT_COPY               1202
 #define IDM_EDIT_DELETE             1203

 #define IDM_OPTIONS_FONT            1701
 #define IDM_OPTIONS_PREFERENCES     1702
 #define IDM_OPTIONS_TOOLBAR         1703
 #define IDM_OPTIONS_SAVEDDIR        1704
 #define IDM_OPTIONS_COLOR           1705
 #define IDM_OPTIONS_SAVEONEXIT      1798
 #define IDM_OPTIONS_SAVENOW         1799

 #define IDM_HELP_CONTENTS           1901
 #define IDM_HELP_SEARCH             1902
 #define IDM_HELP_HOWTOUSE           1903
 #define IDM_HELP_ABOUT              1909

 //-- string ids
 #define IDS_APPNAME                 2000
 #define IDS_SHORT_APPNAME           2001

 #define IDS_OFN_TITLE               2010
 #define IDS_OFN_FILTERNAME          2011
 #define IDS_OFN_FILTER              2012
 #define IDS_OFN_DEBUGGEE_TITLE      2013

 #define IDS_BUTTON_OPEN             2020
 #define IDS_BUTTON_ATTACH           2021
 #define IDS_BUTTON_CUT              2022
 #define IDS_BUTTON_COPY             2023
 #define IDS_BUTTON_DELETE           2024
 #define IDS_BUTTON_HELP             2025

 #define IDS_EXIT_BOX_TITLE          2030
 #define IDS_EXIT_BOX_TEXT           2031

 #define IDS_DEBUG_EVENTS            2040

 #define IDS_API_FAILED_MSG             9001
 #define IDS_WINDOWS_NT_REQUIRED_TITLE  9998
 #define IDS_WINDOWS_NT_REQUIRED        9999

 //-- control ids
 #define IDC_INITIALDIR              9001
 #define IDC_PROCESSLIST             9002
 #define IDC_DEBUG_ONLY_THIS_PROCESS 9003
 #define IDC_DEBUG_PROCESS           9004
 #define IDC_IDLE_PRIORITY_CLASS     9005
 #define IDC_NORMAL_PRIORITY_CLASS   9006
 #define IDC_HIGH_PRIORITY_CLASS     9007
 #define IDC_REALTIME_PRIORITY_CLASS 9008
 #define IDC_CLEAR_ON_NEW            9009
 #define IDC_VERBOSE                 9010
 #define IDC_SHOW_SYMBOLS            9011
 #define IDC_DEBUG_MODE_NONE         9020
 #define IDC_DEBUG_MODE_ERROR        9021
 #define IDC_DEBUG_MODE_MINORERROR   9022
 #define IDC_DEBUG_MODE_WARNING      9023
 #define IDC_SAVE_PREFERENCES        9999

// now defined in winuser.h ... #define IDHELP  9

 //-- user messages
 #ifndef WM_APP
  #define WM_APP 0x8000
 #endif
 #define UM_CREATE_LISTBOX (WM_APP+0)

#endif // DEB_H


DEBDEBUG.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// ************************************************************************
// MODULE    : DEBDebug.C
// PURPOSE   : Debug support functions for the Debug Event Browser
// FUNCTIONS :
//   DebugEventThread()         - debug event processing thread
// COMMENTS  :
//
// ************************************************************************
#define   STRICT               // enable strict typing
#include <Windows.H>           // required for all Windows applications
#include <StdDef.H>            // offsetof()

#include "LinkList.H"          // include the linked list functions
#include "DEBMisc.H"           // include the misc. support functions
#include "DEBDebug.H"          // include the DEB debugging functions

// internal global data
// ------------------------------------------------------------------------
HANDLE hHeap;                  // local heap
PLIST  pProcessList;           // pointer to the process list
BOOL   fFinished = FALSE;      // set to TRUE if the DebugEventThread
                               //  is no longer needed (such as the
                               //  debuggee failed to load or the number
                               //  of debuggee threads goes to zero
                               //  indicating debug session termination

static TCHAR szSourceFileName[] = TEXT(__FILE__);

// internal function prototypes
// ------------------------------------------------------------------------

//-- debug event handling functions
BOOL HandleExceptionEvent( LPDEBUG_EVENT );
BOOL HandleBreakPointException( LPDEBUG_EVENT );
BOOL HandleCreateThreadEvent( LPDEBUG_EVENT );
BOOL HandleCreateProcessEvent( LPDEBUG_EVENT );
BOOL HandleExitThreadEvent( LPDEBUG_EVENT );
BOOL HandleExitProcessEvent( LPDEBUG_EVENT );
BOOL HandleLoadDllEvent( LPDEBUG_EVENT );
BOOL HandleUnloadDllEvent( LPDEBUG_EVENT );
BOOL HandleOutputDebugStringEvent( LPDEBUG_EVENT );
BOOL HandleRipEvent( LPDEBUG_EVENT );
BOOL HandleUnknownEvent( LPDEBUG_EVENT );

//-- misc debug event helper functions
BOOL  DebugNewProcess( LPTSTR, LPTSTR );
BOOL  GetDllFileName( LPDEBUG_EVENT, LPTSTR, DWORD );
BOOL  GetDllFileNameFromList( LPDEBUG_EVENT, LPTSTR, DWORD );
BOOL  GetOutputDebugString( LPDEBUG_EVENT, LPTSTR, DWORD );
DWORD GetModuleFileNameFromHeader( HANDLE, HANDLE, DWORD, LPTSTR, DWORD );
#if defined(_MIPS_) || defined(_ALPHA_) || defined(_PPC_)
 BOOL  SkipBreakPoint( HANDLE );
#endif

//-- linked list wrapper functions

//-- process list and node specific linked list wrapper functions
int  ProcessOrderFunction( PNODE, PNODE );
BOOL CreateProcessList( PLIST* );
BOOL DestroyProcessList( PLIST );
BOOL AllocProcessNode( PNODE*, PDEB_PROCESS_NODE_INFO* );
BOOL InitProcessNodeInfo( PDEB_PROCESS_NODE_INFO*, LPDEBUG_EVENT );
BOOL InsertProcessNode( PLIST, PNODE );
BOOL SetCurrentProcessNode( PLIST, PNODE );
BOOL DeleteProcessNode( PLIST, PNODE );
BOOL FreeProcessNodeInfo( PNODE );
BOOL DestroyProcessNode( PNODE );
BOOL DeleteCurrentProcessNode( PLIST );

//-- thread list and node specific linked list wrapper functions
int  ThreadOrderFunction( PNODE, PNODE );
BOOL CreateThreadList( PLIST* );
BOOL DestroyThreadList( PLIST );
BOOL AllocThreadNode( PNODE*, PDEB_THREAD_NODE_INFO* );
BOOL InitThreadNodeInfo( PDEB_THREAD_NODE_INFO*, LPDEBUG_EVENT );
BOOL InsertThreadNode( PLIST, PNODE );
BOOL SetCurrentThreadNode( PLIST, PNODE );
BOOL DeleteThreadNode( PLIST, PNODE );
BOOL FreeThreadNodeInfo( PNODE );
BOOL DestroyThreadNode( PNODE );
BOOL DeleteCurrentThreadNode( PLIST );

//-- dll list and node specific linked list wrapper functions
int  DllOrderFunction( PNODE, PNODE );
BOOL CreateDllList( PLIST* );
BOOL DestroyDllList( PLIST );
BOOL AllocDllNode( PNODE*, PDEB_DLL_NODE_INFO* );
BOOL InitDllNodeInfo( PDEB_DLL_NODE_INFO*, LPDEBUG_EVENT );
BOOL InsertDllNode( PLIST, PNODE );
BOOL SetCurrentDllNode( PLIST, PNODE );
BOOL DeleteDllNode( PLIST, PNODE );
BOOL FreeDllNodeInfo( PNODE );
BOOL DestroyDllNode( PNODE );
BOOL DeleteCurrentDllNode( PLIST );

// ************************************************************************
// FUNCTION : DebugEventThread( PDEB_STARTUP_INFO )
// PURPOSE  : Main debug event processing loop
// COMMENTS :
//   A new debug event thread is created for each Debuggee process.
//   Return TRUE (non 0) if success, else FALSE (0)
// ************************************************************************
DWORD WINAPI
DebugEventThread( PDEB_STARTUP_INFO pDebStartupInfo )
{
  #define BUFFER_SIZE 256

  static BOOL  fFirstTime = TRUE;
  static TCHAR szDebuggeeTitle[128];

  DEBUG_EVENT  DebugEvent;

  LPTSTR lpszDebugEventBuffer;
  LPTSTR lpszTempBuffer;

  //-- set the minimum error level for debugging events
  SetDebugErrorLevel( Profile.DebugErrorLevel );

  if( fFirstTime ) {
    if( !LoadString( Global.hInstance, IDS_OFN_DEBUGGEE_TITLE, szDebuggeeTitle,
           sizeof(szDebuggeeTitle)/sizeof(TCHAR) ) )
      ErrorMessageBox( TEXT("LoadString()"),
        Global.szApiFailed, szSourceFileName, __LINE__ );
  }

  //-- determine if 'attach to' or 'open new' debuggee
  if( pDebStartupInfo->fActive ) {
    if( !DebugActiveProcess( pDebStartupInfo->dwProcessId ) )
      ErrorMessageBox( TEXT("DebugActiveProcess()"),
        Global.szApiFailed, szSourceFileName, __LINE__ );
  }
  else {
    if( !DebugNewProcess( pDebStartupInfo->lpstrPathName, szDebuggeeTitle ) )
    ExitThread( FALSE );
  }

  //-- increment active process count
  Global.dwActiveDebuggees++;

  //-- create a local heap
  {
    SYSTEM_INFO SysInfo;

    GetSystemInfo( &SysInfo );  // get the system memory page size
    hHeap = HeapCreate( (DWORD) NULL, SysInfo.dwPageSize, 1000*SysInfo.dwPageSize );
  }

  //-- create and initialize the process list
  CreateProcessList( &pProcessList );

  //-- alloc temporary (life of thread) string buffers
  lpszDebugEventBuffer = (LPTSTR) HeapAlloc( hHeap, (DWORD) NULL, BUFFER_SIZE );
  lpszTempBuffer = (LPTSTR) HeapAlloc( hHeap, (DWORD) NULL, BUFFER_SIZE );

  // ----------------------------------------------------------------------
  // begin debug event processing loop
  // ----------------------------------------------------------------------
  for(;;) {

    //-- wait for debug events
    if( !WaitForDebugEvent( &DebugEvent, INFINITE ) ) {
      ListBoxPrintF( pDebStartupInfo->hWndListBox, TEXT( "%s" ), TEXT( "Failed to attach to Debuggee..." ) );
      fFinished = TRUE;
      break;
    }

    // --------------------------------------------------------------------
    // display each debug event as it occurs and handle minimal debug
    // event processing
    // --------------------------------------------------------------------
    MakeCommonDebugEventString( lpszDebugEventBuffer, &DebugEvent );

    switch( DebugEvent.dwDebugEventCode ) {

      // ------------------------------------------------------------------
      // exception occured
      // ------------------------------------------------------------------
      case EXCEPTION_DEBUG_EVENT:

        //-- figure out which type of exception
        switch( DebugEvent.u.Exception.ExceptionRecord.ExceptionCode ) {

          //--standard exceptions
          case EXCEPTION_ACCESS_VIOLATION:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Exception: " ), TEXT( "Access Violation" ) );
            break;

          case EXCEPTION_DATATYPE_MISALIGNMENT:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Exception: " ), TEXT( "Datatype Misalignment" ) );
            break;

          case EXCEPTION_BREAKPOINT:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Exception: " ), TEXT( "Breakpoint" ) );
            break;

          case EXCEPTION_SINGLE_STEP:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Exception: " ), TEXT( "Single Step" ) );
            break;

          case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Exception: " ), TEXT( "Array Bound Exceeded" ) );
            break;

          case EXCEPTION_FLT_DENORMAL_OPERAND:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
              TEXT( "Exception: " ), TEXT( "Floating Point" ),
              TEXT( "Denormal Operand" ) );
            break;

          case EXCEPTION_FLT_DIVIDE_BY_ZERO:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
              TEXT( "Exception: " ), TEXT( "Floating Point" ),
              TEXT( "Divide By Zero" ) );
            break;

          case EXCEPTION_FLT_INEXACT_RESULT:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
              TEXT( "Exception: " ), TEXT( "Floating Point" ),
              TEXT( "Inexact Result" ) );
            break;

          case EXCEPTION_FLT_INVALID_OPERATION:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
              TEXT( "Exception: " ), TEXT( "Floating Point" ),
              TEXT( "Invalid Operation" ) );
            break;

          case EXCEPTION_FLT_OVERFLOW:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
              TEXT( "Exception: " ), TEXT( "Floating Point" ),
              TEXT( "Overflow" ) );
            break;

          case EXCEPTION_FLT_STACK_CHECK:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
              TEXT( "Exception: " ), TEXT( "Floating Point" ),
              TEXT( "Stack Check" ) );
            break;

          case EXCEPTION_FLT_UNDERFLOW:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
              TEXT( "Exception: " ), TEXT( "Floating Point" ),
              TEXT( "Underflow" ) );
            break;

          case EXCEPTION_INT_DIVIDE_BY_ZERO:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
              TEXT( "Exception: " ), TEXT( "Integer" ),
              TEXT( "Divide By Zero" ) );
            break;

          case EXCEPTION_INT_OVERFLOW:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
              TEXT( "Exception: " ), TEXT( "Integer" ),
              TEXT( "Overflow" ) );
            break;

          case EXCEPTION_PRIV_INSTRUCTION:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Exception: " ), TEXT( "Privileged Instruction" ) );
            break;

          case EXCEPTION_IN_PAGE_ERROR:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Exception: " ), TEXT( "In Page Error" ) );
            break;

          //-- Debug exceptions
          case DBG_TERMINATE_THREAD:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Debug Exception: " ), TEXT( "Terminate Thread" ) );
            break;

          case DBG_TERMINATE_PROCESS:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Debug Exception: " ), TEXT( "Terminate Process" ) );
            break;

          case DBG_CONTROL_C:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Debug Exception: " ), TEXT( "Control+C" ) );
            break;

          case DBG_CONTROL_BREAK:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "Debug Exception: " ), TEXT( "Control+Break" ) );
            break;

          //-- RPC exceptions (some)
          case RPC_S_UNKNOWN_IF:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "RPC Exception: " ), TEXT( "Unknown Interface" ) );
            break;

          case RPC_S_SERVER_UNAVAILABLE:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
              TEXT( "RPC Exception: " ), TEXT( "Server Unavailable" ) );
            break;

          //-- VDM exceptions (minimal information)
          case EXCEPTION_VDM_EVENT:  // see DEBDebug.H for definition
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
              TEXT( "VDM Exception: " ) );

          default:
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s%X%s" ),
              TEXT( "Exception: " ), TEXT( "Unknown [0x" ),
               DebugEvent.u.Exception.ExceptionRecord.ExceptionCode,
               TEXT( "]" ) );
            break;

        }
        if( Profile.fVerbose ) {
          StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%d" ),
            TEXT( "dwFirstChance: " ), DebugEvent.u.Exception.dwFirstChance );
        }
        else {
          if( DebugEvent.u.Exception.dwFirstChance != 0 )
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
              TEXT( " - First Chance" ) );
          else
            StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
              TEXT( " - Second Chance" ) );
        }
        HandleExceptionEvent( &DebugEvent );
        break;

      // ------------------------------------------------------------------
      // new thread started
      // ------------------------------------------------------------------
      case CREATE_THREAD_DEBUG_EVENT:
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
          TEXT( "Create Thread: " ) );
        if( Profile.fVerbose ) {
          StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%d\n + %s%X\n + %s%d" ),
            TEXT( "hThread:0x" ), DebugEvent.u.CreateThread.hThread,
            TEXT( "lpThreadLocalBase:0x" ), DebugEvent.u.CreateThread.lpThreadLocalBase,
            TEXT( "lpStartAddress:0x" ), DebugEvent.u.CreateThread.lpStartAddress );
        }
        HandleCreateThreadEvent( &DebugEvent );
        break;

      // ------------------------------------------------------------------
      // new process started
      // ------------------------------------------------------------------
      case CREATE_PROCESS_DEBUG_EVENT:
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
          TEXT( "Create Process: " ) );
        if( Profile.fVerbose ) {
          StringAppendF( lpszDebugEventBuffer,
            TEXT( "\n + %s%X\n + %s%X\n + %s%X\n + %s%X\n + %s%d" )
            TEXT( "\n + %s%d\n + %s%X\n + %s%X\n + %s%X\n + %s%d" ),
            TEXT( "hFile:0x" ), DebugEvent.u.CreateProcessInfo.hFile,
            TEXT( "hProcess:0x" ), DebugEvent.u.CreateProcessInfo.hProcess,
            TEXT( "hThread:0x" ), DebugEvent.u.CreateProcessInfo.hThread,
            TEXT( "lpBaseOfImage:0x" ), DebugEvent.u.CreateProcessInfo.lpBaseOfImage,
            TEXT( "dwDebugInfoFileOffset: " ), DebugEvent.u.CreateProcessInfo.dwDebugInfoFileOffset,
            TEXT( "nDebugInfoSize: " ), DebugEvent.u.CreateProcessInfo.nDebugInfoSize,
            TEXT( "lpThreadLocalBase:0x" ), DebugEvent.u.CreateProcessInfo.lpThreadLocalBase,
            TEXT( "lpStartAddress:0x" ), DebugEvent.u.CreateProcessInfo.lpStartAddress,
            TEXT( "lpImageName:0x" ), DebugEvent.u.CreateProcessInfo.lpImageName,
            TEXT( "fUnicode: " ), DebugEvent.u.CreateProcessInfo.fUnicode );
        }
        HandleCreateProcessEvent( &DebugEvent );
        break;

      // ------------------------------------------------------------------
      // existing thread terminated
      // ------------------------------------------------------------------
      case EXIT_THREAD_DEBUG_EVENT:
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
          TEXT( "Exit Thread: " ) );
        if( Profile.fVerbose ) {
          StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%d" ),
            TEXT( "dwExitCode: " ), DebugEvent.u.ExitThread.dwExitCode );
        }
        else {
          StringAppendF( lpszDebugEventBuffer, TEXT( "%s%d" ),
            TEXT( "Returned " ), DebugEvent.u.ExitThread.dwExitCode );
        }
        HandleExitThreadEvent( &DebugEvent );
        break;

      // ------------------------------------------------------------------
      // existing process terminated
      // ------------------------------------------------------------------
      case EXIT_PROCESS_DEBUG_EVENT:
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
          TEXT( "Exit Process: " ) );
        if( Profile.fVerbose ) {
          StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%d" ),
            TEXT( "dwExitCode: " ), DebugEvent.u.ExitProcess.dwExitCode );
        }
        else {
          StringAppendF( lpszDebugEventBuffer, TEXT( "%s%d" ),
            TEXT( "Returned " ), DebugEvent.u.ExitProcess.dwExitCode );
        }
        HandleExitProcessEvent( &DebugEvent );
        break;

      // ------------------------------------------------------------------
      // new DLL loaded
      // ------------------------------------------------------------------
      case LOAD_DLL_DEBUG_EVENT:
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
          TEXT( "Load DLL: " ) );
        lstrcpy( lpszTempBuffer, TEXT("Empty!") );
        GetDllFileName( &DebugEvent, lpszTempBuffer, BUFFER_SIZE );
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ), lpszTempBuffer );
        if( Profile.fVerbose ) {
          StringAppendF( lpszDebugEventBuffer,
            TEXT( "\n + %s%X\n + %s%X\n + %s%d\n + %s%d\n + %s%X\n + %s%d" ),
            TEXT( "hFile:0x" ), DebugEvent.u.LoadDll.hFile,
            TEXT( "lpBaseOfDll:0x" ), DebugEvent.u.LoadDll.lpBaseOfDll,
            TEXT( "dwDebugInfoFileOffset: " ), DebugEvent.u.LoadDll.dwDebugInfoFileOffset,
            TEXT( "nDebugInfoSize: " ), DebugEvent.u.LoadDll.nDebugInfoSize,
            TEXT( "lpImageName:0x" ), DebugEvent.u.LoadDll.lpImageName,
            TEXT( "fUnicode: " ), DebugEvent.u.LoadDll.fUnicode );
        }
        HandleLoadDllEvent( &DebugEvent );
        break;

      // ------------------------------------------------------------------
      // existing DLL explicitly unloaded
      // ------------------------------------------------------------------
      case UNLOAD_DLL_DEBUG_EVENT:
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ), TEXT( "Unload DLL: " ) );
        GetDllFileNameFromList( &DebugEvent, lpszTempBuffer, BUFFER_SIZE );
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ), lpszTempBuffer );
        if( Profile.fVerbose ) {
          StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%X" ),
            TEXT( "lpBaseOfDLL:0x" ), DebugEvent.u.UnloadDll.lpBaseOfDll );
        }
        HandleUnloadDllEvent( &DebugEvent );
        break;

      // ------------------------------------------------------------------
      // OutputDebugString() occured
      // ------------------------------------------------------------------
      case OUTPUT_DEBUG_STRING_EVENT:
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
          TEXT( "Output Debug String: " ) );
        GetOutputDebugString( &DebugEvent, lpszTempBuffer, BUFFER_SIZE );
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ), lpszTempBuffer );
        if( Profile.fVerbose ) {
          StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%X\n + %s%d\n + %s%d" ),
            TEXT( "lpDebugStringData:0x" ), DebugEvent.u.DebugString.lpDebugStringData,
            TEXT( "fUnicode: " ), DebugEvent.u.DebugString.fUnicode,
            TEXT( "nDebugStringLength: " ), DebugEvent.u.DebugString.nDebugStringLength );
        }
        HandleOutputDebugStringEvent( &DebugEvent );
        break;

      // ------------------------------------------------------------------
      // RIP occured
      // ------------------------------------------------------------------
      case RIP_EVENT:
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
          TEXT( "RIP" ) );
        if( Profile.fVerbose ) {
          StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%d\n + %s%d" ),
            TEXT( "dwError: " ), DebugEvent.u.RipInfo.dwError,
            TEXT( "dwType: " ), DebugEvent.u.RipInfo.dwType );
        }
        HandleRipEvent( &DebugEvent );
        break;

      // ------------------------------------------------------------------
      // unknown debug event occured
      // ------------------------------------------------------------------
      default:
        StringAppendF( lpszDebugEventBuffer, TEXT( "%s%X%s" ),
          TEXT( "Debug Event:Unknown [0x" ),
          DebugEvent.dwDebugEventCode, lpszTempBuffer,
          TEXT( "]" ) );
        HandleUnknownEvent( &DebugEvent );
        break;
    }

    //-- insert the debug event string into the listbox
    ListBoxPrintF( pDebStartupInfo->hWndListBox, TEXT( "%s" ), lpszDebugEventBuffer );

    //-- default action, just continue
    if( fFinished ) {
      fFinished = FALSE;   // reset the value
      break;
    }
    else
      ContinueDebugEvent( DebugEvent.dwProcessId, DebugEvent.dwThreadId,
        DBG_CONTINUE );
  }

  //-- free temporary (life of thread) string buffers
  HeapFree( hHeap, (DWORD) NULL, (PVOID) lpszDebugEventBuffer );
  HeapFree( hHeap, (DWORD) NULL, (PVOID) lpszTempBuffer );

  //-- free list
  DestroyProcessList( pProcessList );

  //-- free the heap
  HeapDestroy( hHeap );

  //-- decrement active process count
  Global.dwActiveDebuggees--;
  ExitThread( TRUE );

  return( TRUE );  // avoid the "no return value" warning
}

// ========================================================================
// debug event handling functions
// ========================================================================

// ************************************************************************
// FUNCTION : HandleExceptionEvent( LPDEBUG_EVENT lpDebugEvent )
// PURPOSE  : handle EXCEPTION_DEBUG_EVENT
// COMMENTS : except for the BreakPoint event, continue and let the
//            application or system exception handlers to the work
// ************************************************************************
BOOL
HandleExceptionEvent( LPDEBUG_EVENT lpDebugEvent )
{
  switch( lpDebugEvent->u.Exception.ExceptionRecord.ExceptionCode ) {

    case EXCEPTION_BREAKPOINT:
      HandleBreakPointException( lpDebugEvent );
      break;

    default:
      ContinueDebugEvent( lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId,
        DBG_EXCEPTION_NOT_HANDLED );

  }

  return( TRUE );
}

// ************************************************************************
// FUNCTION : HandleBreakPointException( LPDEBUG_EVENT lpDebugEvent )
// PURPOSE  : handle the BREAKPOINT exception
// COMMENTS : search process list, search thread list, skip over breakpoint
// ************************************************************************
BOOL
HandleBreakPointException( LPDEBUG_EVENT lpDebugEvent )
{
#if defined(_MIPS_) || defined(_ALPHA_) || defined(_PPC_)
  PNODE                  pProcessNode, pSearchProcessNode;
  PNODE                  pThreadNode, pSearchThreadNode;
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
  PDEB_THREAD_NODE_INFO  pThreadNodeInfo, pSearchThreadNodeInfo;
  PDEB_THREAD_LIST_INFO  pThreadListInfo;

  AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
  pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
  SetCurrentProcessNode( pProcessList, pSearchProcessNode );
  GetCurrentNode( pProcessList, &pProcessNode );
  pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
  pThreadListInfo = (PDEB_THREAD_LIST_INFO) pProcessNodeInfo->pThreadList->pListData;
  //-- if no thread nodes then hThread is stored in the process node
  if( !pThreadListInfo->dwActiveThreads ) {
    SkipBreakPoint( (pProcessNodeInfo->ProcessDebugInfo).hThread );
  }
  else {
    AllocThreadNode( &pSearchThreadNode, &pSearchThreadNodeInfo );
    pSearchThreadNodeInfo->dwThreadId = lpDebugEvent->dwThreadId;
    SetCurrentThreadNode( pProcessNodeInfo->pThreadList, pSearchThreadNode );
    GetCurrentNode( pProcessNodeInfo->pThreadList, &pThreadNode );
    pThreadNodeInfo = (PDEB_THREAD_NODE_INFO) pThreadNode->pNodeData;
    SkipBreakPoint( (pThreadNodeInfo->ThreadDebugInfo).hThread );
    DestroyThreadNode( pSearchThreadNode );
  }
  DestroyProcessNode( pSearchProcessNode );
 #else
  ContinueDebugEvent( lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId,
    DBG_CONTINUE );
 #endif

  return( TRUE );
}

// ************************************************************************
// FUNCTION : HandleCreateThreadEvent( LPDEBUG_EVENT )
// PURPOSE  : handle CREATE_THREAD_DEBUG_EVENT
// COMMENTS : search process list, insert new thread node
// ************************************************************************
BOOL
HandleCreateThreadEvent( LPDEBUG_EVENT lpDebugEvent )
{
  PNODE                  pProcessNode, pSearchProcessNode;
  PNODE                  pThreadNode;
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
  PDEB_THREAD_NODE_INFO  pThreadNodeInfo;

  AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
  pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
  SetCurrentProcessNode( pProcessList, pSearchProcessNode );
  GetCurrentNode( pProcessList, &pProcessNode );
  pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
  AllocThreadNode( &pThreadNode, &pThreadNodeInfo );
  InitThreadNodeInfo( &pThreadNodeInfo, lpDebugEvent );
  InsertThreadNode( pProcessNodeInfo->pThreadList, pThreadNode );
  DestroyProcessNode( pSearchProcessNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : HandleCreateProcessEvent( LPDEBUG_EVENT )
// PURPOSE  : handle CREATE_PROCESS_DEBUG_EVENT
// COMMENTS : insert new process node
// ************************************************************************
BOOL
HandleCreateProcessEvent( LPDEBUG_EVENT lpDebugEvent )
{
  PNODE                  pProcessNode;
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo;

  AllocProcessNode( &pProcessNode, &pProcessNodeInfo );
  InitProcessNodeInfo( &pProcessNodeInfo, lpDebugEvent );
  InsertProcessNode( pProcessList, pProcessNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : HandleExitThreadEvent( LPDEBUG_EVENT )
// PURPOSE  : handle EXIT_THREAD_DEBUG_EVENT
// COMMENTS : search process list, search thread list, delete existing
//            thread node
// ************************************************************************
BOOL
HandleExitThreadEvent( LPDEBUG_EVENT lpDebugEvent )
{
  PNODE                  pProcessNode, pSearchProcessNode;
  PNODE                  pSearchThreadNode;
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
  PDEB_THREAD_NODE_INFO  pSearchThreadNodeInfo;

  AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
  pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
  SetCurrentProcessNode( pProcessList, pSearchProcessNode );
  GetCurrentNode( pProcessList, &pProcessNode );
  pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
  AllocThreadNode( &pSearchThreadNode, &pSearchThreadNodeInfo );
  pSearchThreadNodeInfo->dwThreadId = lpDebugEvent->dwThreadId;
  DeleteThreadNode( pProcessNodeInfo->pThreadList, pSearchThreadNode );
  DestroyThreadNode( pSearchThreadNode );
  DestroyProcessNode( pSearchProcessNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : HandleExitProcessEvent( LPDEBUG_EVENT )
// PURPOSE  : handle EXIT_PROCESS_DEBUG_EVENT
// COMMENTS : search process list, delete existing process node,
// ************************************************************************
BOOL
HandleExitProcessEvent( LPDEBUG_EVENT lpDebugEvent )
{
  PNODE                  pSearchProcessNode;
  PDEB_PROCESS_NODE_INFO pSearchProcessNodeInfo;
  PDEB_PROCESS_LIST_INFO pProcessListInfo;

  AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
  pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
  DeleteProcessNode( pProcessList, pSearchProcessNode );
  //-- if last process? free all temporary memory, exit thread
  pProcessListInfo = (PDEB_PROCESS_LIST_INFO) pProcessList->pListData;
  if( !pProcessListInfo->dwActiveProcesses )
    fFinished = TRUE;
  DestroyProcessNode( pSearchProcessNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : HandleLoadDllEvent( LPDEBUG_EVENT )
// PURPOSE  : handle LOAD_DLL_DEBUG_EVENT
// COMMENTS : search process list, insert new DLL node
// ************************************************************************
BOOL
HandleLoadDllEvent( LPDEBUG_EVENT lpDebugEvent )
{
  PNODE                  pProcessNode, pSearchProcessNode;

PNODE                  pDllNode;
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
  PDEB_DLL_NODE_INFO     pDllNodeInfo;

  AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
  pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
  SetCurrentProcessNode( pProcessList, pSearchProcessNode );
  GetCurrentNode( pProcessList, &pProcessNode );
  pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
  AllocDllNode( &pDllNode, &pDllNodeInfo );
  InitDllNodeInfo( &pDllNodeInfo, lpDebugEvent );
  InsertDllNode( pProcessNodeInfo->pDllList, pDllNode );
  DestroyProcessNode( pSearchProcessNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : HandleUnloadDllEvent( LPDEBUG_EVENT )
// PURPOSE  : handle UNLOAD_DLL_DEBUG_EVENT
// COMMENTS : search process list, search DLL list, delete existing DLL
//            node
// ************************************************************************
BOOL
HandleUnloadDllEvent( LPDEBUG_EVENT lpDebugEvent )
{
  PNODE                  pProcessNode, pSearchProcessNode;
  PNODE                  pSearchDllNode;
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
  PDEB_DLL_NODE_INFO     pSearchDllNodeInfo;

  AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
  pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
  SetCurrentProcessNode( pProcessList, pSearchProcessNode );
  GetCurrentNode( pProcessList, &pProcessNode );
  pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
  AllocDllNode( &pSearchDllNode, &pSearchDllNodeInfo );
  pSearchDllNodeInfo->DllDebugInfo.lpBaseOfDll = lpDebugEvent->u.UnloadDll.lpBaseOfDll;
  DeleteDllNode( pProcessNodeInfo->pDllList, pSearchDllNode );
  DestroyDllNode( pSearchDllNode );
  DestroyProcessNode( pSearchProcessNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : HandleOutputDebugStringEvent( LPDEBUG_EVENT )
// PURPOSE  : handle OUTPUT_DEBUG_STRING_EVENT
// COMMENTS : do nothing
// ************************************************************************
BOOL
HandleOutputDebugStringEvent( LPDEBUG_EVENT lpDebugEvent )
{
  return( TRUE );
}

// ************************************************************************
// FUNCTION : HandleRipEvent( LPDEBUG_EVENT )
// PURPOSE  : handle RIP_EVENT
// COMMENTS : do nothing
// ************************************************************************
BOOL
HandleRipEvent( LPDEBUG_EVENT lpDebugEvent )
{
  return( TRUE );
}

// ************************************************************************
// FUNCTION : HandleUnknownEvent( LPDEBUG_EVENT )
// PURPOSE  : handle all unknown debug events
// COMMENTS : do nothing
// ************************************************************************
BOOL
HandleUnknownEvent( LPDEBUG_EVENT lpDebugEvent )
{
  return( TRUE );
}

// ========================================================================
// misc debug event helper functions
// ========================================================================

// ************************************************************************
// FUNCTION : DebugNewProcess( LPTSTR, LPTSTR )
// PURPOSE  : starts a new process as a debuggee
// COMMENTS :
// ************************************************************************
BOOL
DebugNewProcess( LPTSTR lpszFileName, LPTSTR lpszTitle )
{
  static STARTUPINFO           StartupInfo;
  static LPSTARTUPINFO         lpStartupInfo = &StartupInfo;
  static PROCESS_INFORMATION   ProcessInfo;
  static LPPROCESS_INFORMATION lpProcessInfo = &ProcessInfo;

  lpStartupInfo->cb          = sizeof( STARTUPINFO );
  lpStartupInfo->lpDesktop   = NULL;
  lpStartupInfo->lpTitle     = lpszTitle;
  lpStartupInfo->dwX         = 0;
  lpStartupInfo->dwY         = 0;
  lpStartupInfo->dwXSize     = 0;
  lpStartupInfo->dwYSize     = 0;
  lpStartupInfo->dwFlags     = (DWORD) NULL;
  lpStartupInfo->wShowWindow = SW_SHOWDEFAULT;

  lpProcessInfo->hProcess = NULL;

  //-- create the Debuggee process instead
  if( !CreateProcess(
         NULL,
         lpszFileName,
         (LPSECURITY_ATTRIBUTES) NULL,
         (LPSECURITY_ATTRIBUTES) NULL,
         TRUE,
         Profile.DebugMode | Profile.DebuggeePriority | CREATE_NEW_CONSOLE,
         (LPVOID) NULL,
         (LPTSTR) NULL,
         lpStartupInfo, lpProcessInfo ) ) {

    switch( GetLastError() ) {

      case ERROR_FILE_NOT_FOUND:
        MessageBox( GetDesktopWindow(), TEXT( "This file does not exist." ),
          TEXT( "Open File Error" ), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
        break;
      case ERROR_ACCESS_DENIED:
        MessageBox( GetDesktopWindow(), TEXT( "Access denied." ),
          TEXT( "Open File Error" ), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
        break;
      case ERROR_FILE_INVALID:
        MessageBox( GetDesktopWindow(), TEXT( "Invalid file." ),
          TEXT( "Open File Error" ), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
        break;
      case ERROR_FILE_CORRUPT:
        MessageBox( GetDesktopWindow(), TEXT( "The file is corrupt." ),
          TEXT( "Open File Error" ), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
        break;
      case ERROR_BAD_EXE_FORMAT:
        MessageBox( GetDesktopWindow(), TEXT( "The file has a bad format." ),
          TEXT( "Open File Error" ), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
        break;
      default:
        ErrorMessageBox( TEXT( "CreateProcess()" ),
          Global.szApiFailed, szSourceFileName, __LINE__ );
        break;

    }
    return( FALSE );

  }
  else {
    CloseHandle( ProcessInfo.hProcess );
    CloseHandle( ProcessInfo.hThread );
  }

  return( TRUE
 );
}

// ************************************************************************
// FUNCTION : GetDllFileName( LPDEBUG_EVENT, LPTSTR, DWORD )
// PURPOSE  : get DLL filename when LOAD_DLL_DEBUG_EVENT occurs
// COMMENTS : search process list, get DLL name from header
// ************************************************************************
BOOL
GetDllFileName( LPDEBUG_EVENT lpDebugEvent, LPTSTR lpszBuffer,
  DWORD cchBuffer )
{
  PNODE                  pProcessNode, pSearchProcessNode;
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;

  AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
  pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
  SetCurrentProcessNode( pProcessList, pSearchProcessNode );
  GetCurrentNode( pProcessList, &pProcessNode );
  pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
  GetModuleFileNameFromHeader(
    pProcessNodeInfo->ProcessDebugInfo.hProcess,
    lpDebugEvent->u.LoadDll.hFile,
    (DWORD) lpDebugEvent->u.LoadDll.lpBaseOfDll,
    lpszBuffer, cchBuffer);
  DestroyProcessNode( pSearchProcessNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : GetDllFileNameFromList( LPDEBUG_EVENT, LPTSTR, DWORD )
// PURPOSE  : get DLL filename when UNLOAD_DLL_DEBUG_EVENT occurs
// COMMENTS : search process list, search DLL list, get DLL name
// ************************************************************************
BOOL
GetDllFileNameFromList( LPDEBUG_EVENT lpDebugEvent, LPTSTR lpszBuffer,
  DWORD cchBuffer )
{
  PNODE                  pProcessNode, pSearchProcessNode;
  PNODE                  pDllNode, pSearchDllNode;
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
  PDEB_DLL_NODE_INFO     pDllNodeInfo, pSearchDllNodeInfo;

  UNREFERENCED_PARAMETER( cchBuffer );

  AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
  pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
  SetCurrentProcessNode( pProcessList, pSearchProcessNode );
  GetCurrentNode( pProcessList, &pProcessNode );
  pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
  AllocDllNode( &pSearchDllNode, &pSearchDllNodeInfo );
  pSearchDllNodeInfo->DllDebugInfo.lpBaseOfDll = lpDebugEvent->u.UnloadDll.lpBaseOfDll;
  SetCurrentDllNode( pProcessNodeInfo->pDllList, pSearchDllNode );
  GetCurrentNode( pProcessNodeInfo->pDllList, &pDllNode );
  pDllNodeInfo = (PDEB_DLL_NODE_INFO) pDllNode->pNodeData;
  lstrcpy( lpszBuffer, pDllNodeInfo->lpstrFileName );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : GetOutputDebugString( LPDEBUG_EVENT, LPTSTR, DWORD )
// PURPOSE  : get the output debug string from the debuggee when
//            OUTPUT_DEBUG_STRING_EVENT occurs
// COMMENTS : search process list, read the string from the debuggee
// ************************************************************************
BOOL
GetOutputDebugString( LPDEBUG_EVENT lpDebugEvent, LPTSTR lpszBuffer,
  DWORD cchBuffer )
{
  PNODE                  pProcessNode, pSearchProcessNode;
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
  DWORD dwNumberOfBytesRead;

  UNREFERENCED_PARAMETER( cchBuffer );

  AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
  pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
  SetCurrentProcessNode( pProcessList, pSearchProcessNode );
  GetCurrentNode( pProcessList, &pProcessNode );
  pProcessNode = (PNODE) pProcessList->pCurrentNode;
  pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
  ReadProcessMemory(
    pProcessNodeInfo->ProcessDebugInfo.hProcess,
    lpDebugEvent->u.DebugString.lpDebugStringData,
    lpszBuffer, lpDebugEvent->u.DebugString.nDebugStringLength,
    &dwNumberOfBytesRead );
  DestroyProcessNode( pSearchProcessNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : GetModuleFileNameFromHeader( HANDLE, HANDLE, DWORD, LPTSTR, DWORD )
// PURPOSE  : Retrieves the DLL module name for a given file handle of a
//            the module.  Reads the module name from the EXE header.
// COMMENTS :
//   Retrieves only the module name and not the pathname.  Returns the
//   number of characters copies to the buffer, else returns 0.
// ************************************************************************
DWORD
GetModuleFileNameFromHeader( HANDLE hProcess, HANDLE hFile, DWORD BaseOfDll,
  LPTSTR lpszPath, DWORD cchPath )
{
  #define IMAGE_SECOND_HEADER_OFFSET    (15 * sizeof(ULONG)) // relative to file beginning
  #define IMAGE_BASE_OFFSET             (13 * sizeof(DWORD)) // relative to PE header base
  #define IMAGE_EXPORT_TABLE_RVA_OFFSET (30 * sizeof(DWORD)) // relative to PE header base
  #define IMAGE_NAME_RVA_OFFSET         offsetof(IMAGE_EXPORT_DIRECTORY, Name)

  WORD   DosSignature;
  DWORD  NtSignature;
  DWORD  dwNumberOfBytesRead = 0;
  DWORD  PeHeader, ImageBase, ExportTableRVA, NameRVA;

  //-- verify that the handle is not NULL
  if( !hFile ) {
    lstrcpy( lpszPath, TEXT("Invalid File Handle") );
    return( 0 );
  }

  //-- verify that the handle is for a disk file
  if( GetFileType(hFile) != FILE_TYPE_DISK ) {
    lstrcpy( lpszPath, TEXT("Invalid File Type") );
    return( 0 );
  }

  //-- Extract the filename from the EXE header
  SetFilePointer( hFile, 0L, NULL, FILE_BEGIN );
  ReadFile( hFile, &DosSignature, sizeof(DosSignature), &dwNumberOfBytesRead,
    (LPOVERLAPPED) NULL);

  //-- verify DOS signature found
  if( DosSignature != IMAGE_DOS_SIGNATURE ) {
    wsprintf( lpszPath, TEXT( "Bad MZ Signature: 0x%x" ), DosSignature );
    return( 0 );
  }

  SetFilePointer( hFile, IMAGE_SECOND_HEADER_OFFSET, (LPLONG) NULL,
    FILE_BEGIN );
  ReadFile( hFile, &PeHeader, sizeof(PeHeader), &dwNumberOfBytesRead,
    (LPOVERLAPPED) NULL );
  SetFilePointer( hFile, PeHeader, (LPLONG) NULL, FILE_BEGIN );
  ReadFile( hFile, &NtSignature, sizeof(NtSignature), &dwNumberOfBytesRead,
    (LPOVERLAPPED) NULL);

  //-- verify Windows NT (PE) signature found
  if( NtSignature != IMAGE_NT_SIGNATURE ) {
    wsprintf( lpszPath, TEXT( "Bad PE Signature: 0x%x" ), DosSignature );
    return( 0 );
  }

  SetFilePointer( hFile, PeHeader + IMAGE_BASE_OFFSET, (LPLONG) NULL,
    FILE_BEGIN );
  ReadFile( hFile, &ImageBase, sizeof(ImageBase), &dwNumberOfBytesRead,
    (LPOVERLAPPED) NULL);
  SetFilePointer( hFile, PeHeader + IMAGE_EXPORT_TABLE_RVA_OFFSET,
    (LPLONG) NULL, FILE_BEGIN );
  ReadFile( hFile, &ExportTableRVA, sizeof(ExportTableRVA),
    &dwNumberOfBytesRead, (LPOVERLAPPED) NULL);

  //-- now read from the virtual address space in the process
  ReadProcessMemory( hProcess,
     (LPVOID) (BaseOfDll + ExportTableRVA + IMAGE_NAME_RVA_OFFSET),
     &NameRVA, sizeof(NameRVA), &dwNumberOfBytesRead );
  lstrcpy( lpszPath, TEXT("Empty!") );
  if( !ReadProcessMemory( hProcess,
         (LPVOID) (BaseOfDll + NameRVA),
         lpszPath, cchPath, &dwNumberOfBytesRead ) )
     lstrcpy( lpszPath, TEXT("Access Denied!") );

  return( dwNumberOfBytesRead );
}

#if defined(_MIPS_) || defined(_ALPHA_) || defined(_PPC_)
// ************************************************************************
// FUNCTION : SkipThreadBreakPoint( HANDLE );
// PURPOSE  : Skip over the break point instruction belonging to
//            hThread.
// COMMENTS :
//   Only the MIPS R4x00 and DEC Alpha AXP require this.
// ************************************************************************
BOOL
SkipBreakPoint( HANDLE hThread )
{
  static CONTEXT Context;

  Context.ContextFlags = CONTEXT_CONTROL;
  if( !GetThreadContext( hThread, &Context ) )
    return( FALSE );
#if defined(_PPC_)
  Context.Iar += 4L;  // Iar is the PC (program counter)
#else
  Context.Fir += 4L;  // Fir is the PC (program counter)
                      // BREAK (breakpoint instruction) occupies 4 bytes
#endif

  // -----------------------------------------------------------------------
  //  Below would be equivalent for the Intel 80x86 (if it were necessary)
  //  however the Intel x86 automatically increments ip past the 'int 3':
  //  Context.Eip += 1L;  // Eip is the PC (program counter)
  //  // int 3 (breakpoint instruction) occupies 1 byte
  // -----------------------------------------------------------------------

  if( !SetThreadContext( hThread, &Context ) )
    return( FALSE );

  return( TRUE );
}
#endif

// ========================================================================
// wrapper functions to the linked list services
// ========================================================================

// ========================================================================
// Debug Event Browser Data Structure Overview
// -------------------------------------------
//
// The Debug Event Browser (DEB) maintains a rather involved data structure
// to store various debug event and debuggee process information.  It
// attempts to encapsulate the intricacies of what makes a process based on
// the occuring events.  Much of this stored information is never utilized
// by the Debug Event Browser but it is included to demonstrate what types
// of debug event may be useful to a full blown debugger application.
//
// This data structure uses the generalized, sorted, double-linked list
// package provided with the sample.  Each list can store list-specific
// instance data, list-specific node data, and maintain various pointers
// to these nodes.  The list is sorted via the insertion sort method where
// the programmer defines the list-specific sort function whose purpose is
// to compare two given nodes and return their relative sort location.  The
// list package in generalized in the sense that the list and node-specific
// data type is not known to this package at compile time or at runtime.
// The application programmer is merely responsible for defining the list
// and node-specific data structures and the sorting and optional searching
// functions and the list package keeps track of these nodes and provides
// easy access to them.
//
// DEB uses this list package to create three unique list types: process,
// thread and DLL lists.
//
// The backbone of the data structure is the process list.  The nodes of
// the process list are the individual debuggee processes.  DEB allows
// debugging (or should I say debug event browsing) of other processes that
// are spawned by the initial debuggee.  Thus each debug session may have
// multiple debuggees and thus the process becomes the logical node unit.
//
// A visual diagram of the process list is as follows:
//
//                 (ProcessList)
//                      |
//                      |
//                      v
//                 +-----------------+           +----------------------+
//                 | -ProcessList-   |           | -ListData-           |
//                 |                 |           |                      |
//                 | ListData--------+---------->| ActiveProcessCount=N |
//           +-----+-FirstNode       |           +----------------------+
//           |   +-+ CurrentNode     |
//           |   | | LastNode--------+-----------------------+
//           |   | | OrderFunction=& |                       |
//           |   | | ListError=0     |                       |
//           |   | +-----------------+                       |
//           |   |                                           |
//           |   +-------------------+                       |
//           |                       |                       |
//           v                       v                       v
//         +------------+     +------------+             +------------+
// NULL <- |ProcessNode1| <=> |ProcessNode2| <=> ... <=> |ProcessNodeN| -> NULL
//         +------------+     +------------+             +------------+
//
// Each process node also contains two lists: the thread list and the DLL list.
// This node also stores some of the relevent debug event information
// particular to the create process event.  Visually it is as follows:
//
//    +------------------+         +--------------+
//    | -ProcessNode-    | +------>| -ThreadList- |
//    |                  | |       +--------------+
//    | ProcessID=0      | |
//    | ThreadID=0       | |       +-----------+
//    | FileName=""      | |   +-->| -DllList- |
//    | PathName=""      | |   |   +-----------+
//    | ThreadList-------+-+   |
//    | DllList----------+-----+   +-------------------+
//    | ProcessDebugInfo-+-------->|-ProcessDebugInfo- |
//    +------------------+         +-------------------+
//
// Much like the process list, the visual diagram of the thread list is as
// follows:
//
//                 +-----------------+           +---------------------+
//                 | -ThreadList-    |           | -ListData-          |
//                 |                 |           |                     |
//                 | ListData--------+---------->| ActiveThreadCount=N |
//           +-----+-FirstNode       |           +---------------------+
//           |   +-+ CurrentNode     |
//           |   | | LastNode--------+-----------------------+
//           |   | | OrderFunction=& |                       |
//           |   | | ListError=0     |                       |
//           |   | +-----------------+                       |
//           |   |                                           |
//           |   +-------------------+                       |
//           |                       |                       |
//           v                       v                       v
//          +-----------+     +-----------+             +-----------+
//  NULL <- |ThreadNode1| <=> |ThreadNode2| <=> ... <=> |ThreadNodeN| -> NULL
//          +-----------+     +-----------+             +-----------+
//
// The thread nodes store some of the relevent debug event information
// particular to the create thread event.  Visually it is as follows:
//
//    +--------------------+
//    | -ThreadNode-       |
//    |                    |
//    | ProcessID=0        |
//    | ThreadID=0         |
//    | ThreadDebugInfo={} |
//    +--------------------+
//
// Much like the process and thread lists, the visual diagram of the Dll list
// is as follows:
//
//                 +-----------------+           +------------------+
//                 | -DllList-       |           | -ListData-       |
//                 |                 |           |                  |
//                 | ListData--------+---------->| ActiveDllCount=N |
//           +-----+-FirstNode       |           +------------------+
//           |   +-+ CurrentNode     |
//           |   | | LastNode--------+-----------------+
//           |   | | OrderFunction=& |                 |
//           |   | | ListError=0     |                 |
//           |   | +-----------------+                 |
//           |   |                                     |
//           |   +-------------+                       |
//           |                 |                       |
//           v                 v                       v
//          +--------+     +--------+             +--------+
//  NULL <- |DllNode1| <=> |DllNode2| <=> ... <=> |DllNodeN| -> NULL
//          +--------+     +--------+             +--------+
//
// The Dll nodes store some of the relevent debug event information particular
// to the Dll load event.  Visually it is as follows:
//
//    +----------------+
//    | -DllNode-      |
//    |                |
//    | FileName=""    |
//    | PathName=""    |
//    | DllDebugInfo={}|
//    +----------------+
//
// ========================================================================

// ------------------------------------------------------------------------
// Process list and node specific linked list wrapper functions
// ------------------------------------------------------------------------

// ************************************************************************
// FUNCTION : ProcessOrderFunction( PNODE, PNODE );
// PURPOSE  : Provides the sorting/search logic for the double linked
//            list package.
// COMMENTS :
//   Sorted by process ID value
// ************************************************************************
int
ProcessOrderFunction( PNODE pNode1, PNODE pNode2 )
{
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo1 = pNode1->pNodeData;
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo2 = pNode2->pNodeData;

  if( pProcessNodeInfo1->dwProcessId < pProcessNodeInfo2->dwProcessId )
    return( LIST_LEFT_OF );

  if( pProcessNodeInfo1->dwProcessId > pProcessNodeInfo2->dwProcessId )
    return( LIST_RIGHT_OF );

  return( LIST_MATCH );
}

// ************************************************************************
// FUNCTION : CreateProcessList( PLIST* )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
CreateProcessList( PLIST* ppProcessList )
{
  PDEB_PROCESS_LIST_INFO pProcessListInfo;

  //-- create list
  CreateList( ppProcessList, ProcessOrderFunction );

  //-- alloc info data
  pProcessListInfo = (PDEB_PROCESS_LIST_INFO) HeapAlloc( hHeap, (DWORD) NULL,
                                                sizeof( DEB_PROCESS_LIST_INFO ) );
  (*ppProcessList)->pListData = pProcessListInfo;

  //-- init info data
  pProcessListInfo->dwActiveProcesses = 0;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DestroyProcessList( PLIST )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
DestroyProcessList( PLIST pProcessList )
{
  PDEB_PROCESS_LIST_INFO pProcessListInfo = pProcessList->pListData;
  PNODE                  pDeleteNode;

  //-- make sure all nodes are removed first
  while( pProcessListInfo->dwActiveProcesses ) {
    GetCurrentNode( pProcessList, &pDeleteNode );
    DeleteCurrentProcessNode( pProcessList );
  }

  //-- free info data
  HeapFree( hHeap, (DWORD) NULL, (PVOID) pProcessListInfo );

  //-- destroy list
  DestroyList( pProcessList );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : AllocProcessNode( PNODE*, PDEB_PROCESS_NODE_INFO* )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
AllocProcessNode( PNODE* ppProcessNode, PDEB_PROCESS_NODE_INFO* ppProcessNodeInfo )
{
  //-- create node
  CreateNode( ppProcessNode );

  //-- alloc info data
  *ppProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) HeapAlloc( hHeap, (DWORD) NULL,
                                              sizeof( DEB_PROCESS_NODE_INFO ) );
  (*ppProcessNode)->pNodeData = *(ppProcessNodeInfo);

  (*ppProcessNodeInfo)->lpstrFileName = (LPTSTR) HeapAlloc( hHeap, (DWORD) NULL,
                                               (DWORD) MAX_PATH );
  (*ppProcessNodeInfo)->lpstrPathName = (LPTSTR) HeapAlloc( hHeap, (DWORD) NULL,
                                               (DWORD) MAX_PATH );
  CreateThreadList( &((*ppProcessNodeInfo)->pThreadList) );
  CreateDllList( &((*ppProcessNodeInfo)->pDllList) );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : InitProcessNodeInfo( PDEB_PROCESS_NODE_INFO*, LPDEBUG_EVENT )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
InitProcessNodeInfo( PDEB_PROCESS_NODE_INFO* ppProcessNodeInfo, LPDEBUG_EVENT lpDebugEvent )
{
  //-- init info data
  (*ppProcessNodeInfo)->dwProcessId = lpDebugEvent->dwProcessId;
  (*ppProcessNodeInfo)->dwThreadId  = lpDebugEvent->dwThreadId;
  // Note:pThreadList initialized via previous CreateThreadList() call
  // Note:pDllList initialized via previous CreateDllList() call
  (*ppProcessNodeInfo)->ProcessDebugInfo = lpDebugEvent->u.CreateProcessInfo;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : InsertProcessNode( PLIST, PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
InsertProcessNode( PLIST pProcessList, PNODE pProcessNode )
{
  PDEB_PROCESS_LIST_INFO pProcessListInfo = pProcessList->pListData;

  //-- insert the node
  InsertNode( pProcessList, pProcessNode );

  //-- increment dwActiveProcesss
  pProcessListInfo->dwActiveProcesses++;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : SetCurrentProcessNode( PLIST, PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
SetCurrentProcessNode( PLIST pProcessList, PNODE pProcessNode )
{
  SetCurrentNode( pProcessList, pProcessNode, pProcessList->OrderFunction );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DeleteProcessNode( PLIST, PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
DeleteProcessNode( PLIST pProcessList, PNODE pProcessNode )
{
  PNODE pDeleteNode;

  SetCurrentNode( pProcessList, pProcessNode, pProcessList->OrderFunction );
  GetCurrentNode( pProcessList, &pDeleteNode );
  DeleteCurrentProcessNode( pProcessList );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : FreeProcessNodeInfo( PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
FreeProcessNodeInfo( PNODE pProcessNode )
{
  PDEB_PROCESS_NODE_INFO pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;

  //-- free info data
  DestroyDllList( pProcessNodeInfo->pDllList );
  DestroyThreadList( pProcessNodeInfo->pThreadList );
  HeapFree( hHeap, (DWORD) NULL, (PVOID) pProcessNodeInfo->lpstrPathName );
  HeapFree( hHeap, (DWORD) NULL, (PVOID) pProcessNodeInfo->lpstrFileName );
  HeapFree( hHeap, (DWORD) NULL, (PVOID) pProcessNodeInfo );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DestroyProcessNode( PNODE )
// PURPOSE  :
// COMMENTS : Frees all memory associated with the node.
// ************************************************************************
BOOL
DestroyProcessNode( PNODE pProcessNode )
{
  //-- free info data
  FreeProcessNodeInfo( pProcessNode );

  //-- destroy node
  DestroyNode( pProcessNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DeleteCurrentProcessNode( PLIST )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
DeleteCurrentProcessNode( PLIST pProcessList )
{
  PDEB_PROCESS_LIST_INFO pProcessListInfo = pProcessList->pListData;
  PNODE                  pProcessNode = (PNODE) pProcessList->pCurrentNode;

  //-- free info data
  FreeProcessNodeInfo( pProcessNode );

  //-- delete and destroy node
  DeleteCurrentNode( pProcessList );

  //-- decrement dwActiveProcesss
  pProcessListInfo->dwActiveProcesses--;

  return( TRUE );
}

// ------------------------------------------------------------------------
// Thread list and node specific linked list wrapper functions
// ------------------------------------------------------------------------

// ************************************************************************
// FUNCTION : ThreadOrderFunction( PNODE, PNODE );
// PURPOSE  : Provides the sorting/search logic for the double linked
//            list package.
// COMMENTS :
//   Sorted by thread ID value
// ************************************************************************
int
ThreadOrderFunction( PNODE pNode1, PNODE pNode2 )
{
  PDEB_THREAD_NODE_INFO pThreadNodeInfo1 = pNode1->pNodeData;
  PDEB_THREAD_NODE_INFO pThreadNodeInfo2 = pNode2->pNodeData;

  if( pThreadNodeInfo1->dwThreadId < pThreadNodeInfo2->dwThreadId )
    return( LIST_LEFT_OF );

  if( pThreadNodeInfo1->dwThreadId > pThreadNodeInfo2->dwThreadId )
    return( LIST_RIGHT_OF );

  return( LIST_MATCH );
}

// ************************************************************************
// FUNCTION : CreateThreadList( PLIST* )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
CreateThreadList( PLIST* ppThreadList )
{
  PDEB_THREAD_LIST_INFO pThreadListInfo;

  //-- create list
  CreateList( ppThreadList, ThreadOrderFunction );

  //-- alloc info data
  pThreadListInfo = (PDEB_THREAD_LIST_INFO) HeapAlloc( hHeap, (DWORD) NULL,
                                                sizeof( DEB_THREAD_LIST_INFO ) );
  (*ppThreadList)->pListData = pThreadListInfo;

  //-- init info data
  pThreadListInfo->dwActiveThreads = 0;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DestroyThreadList( PLIST )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
DestroyThreadList( PLIST pThreadList )
{
  PDEB_THREAD_LIST_INFO pThreadListInfo = pThreadList->pListData;
  PNODE                 pDeleteNode;

  //-- make sure all nodes are removed first
  while( pThreadListInfo->dwActiveThreads ) {
    GetCurrentNode( pThreadList, &pDeleteNode );
    DeleteCurrentThreadNode( pThreadList );
  }

  //-- free info data
  HeapFree( hHeap, (DWORD) NULL, (PVOID) pThreadListInfo );

  //-- destroy list
  DestroyList( pThreadList );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : AllocThreadNode( PNODE*, PDEB_THREAD_NODE_INFO* )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
AllocThreadNode( PNODE* ppThreadNode, PDEB_THREAD_NODE_INFO* ppThreadNodeInfo )
{
  //-- create node
  CreateNode( ppThreadNode );

  //-- alloc info data
  *ppThreadNodeInfo = (PDEB_THREAD_NODE_INFO) HeapAlloc( hHeap, (DWORD) NULL,
                                            (DWORD) sizeof( DEB_THREAD_NODE_INFO ) );
  (*ppThreadNode)->pNodeData = *(ppThreadNodeInfo);

  return( TRUE );
}

// ************************************************************************
// FUNCTION : InitThreadNodeInfo( PDEB_THREAD_NODE_INFO*, LPDEBUG_EVENT )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
InitThreadNodeInfo( PDEB_THREAD_NODE_INFO* ppThreadNodeInfo,
  LPDEBUG_EVENT lpDebugEvent )
{
  //-- init info data
  (*ppThreadNodeInfo)->dwProcessId     = lpDebugEvent->dwProcessId;
  (*ppThreadNodeInfo)->dwThreadId      = lpDebugEvent->dwThreadId;
  (*ppThreadNodeInfo)->ThreadDebugInfo = lpDebugEvent->u.CreateThread;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : InsertThreadNode( PLIST, PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
InsertThreadNode( PLIST pThreadList, PNODE pThreadNode )
{
  PDEB_THREAD_LIST_INFO pThreadListInfo = (PDEB_THREAD_LIST_INFO) pThreadList->pListData;

  //-- insert the thread node
  InsertNode( pThreadList, pThreadNode );

  //-- increment dwActiveThreads
  pThreadListInfo->dwActiveThreads++;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : SetCurrentThreadNode( PLIST, PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
SetCurrentThreadNode( PLIST pThreadList, PNODE pThreadNode )
{
  SetCurrentNode( pThreadList, pThreadNode, pThreadList->OrderFunction );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DeleteThreadNode( PLIST, PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
DeleteThreadNode( PLIST pThreadList, PNODE pThreadNode )
{
  PNODE pDeleteNode;

  SetCurrentNode( pThreadList, pThreadNode, pThreadList->OrderFunction );
  GetCurrentNode( pThreadList, &pDeleteNode );
  DeleteCurrentThreadNode( pThreadList );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : FreeThreadNodeInfo( PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
FreeThreadNodeInfo( PNODE pThreadNode )
{
  PDEB_THREAD_NODE_INFO pThreadNodeInfo = (PDEB_THREAD_NODE_INFO) pThreadNode->pNodeData;

  //-- free info data
  HeapFree( hHeap, (DWORD) NULL, (PVOID) pThreadNodeInfo );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DestroyThreadNode( PNODE )
// PURPOSE  :
// COMMENTS : Frees all memory associated with the node.
// ************************************************************************
BOOL
DestroyThreadNode( PNODE pThreadNode )
{
  //-- free info data
  FreeThreadNodeInfo( pThreadNode );

  //-- destroy node
  DestroyNode( pThreadNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DeleteCurrentThreadNode( PLIST )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
DeleteCurrentThreadNode( PLIST pThreadList )
{
  PDEB_THREAD_LIST_INFO pThreadListInfo = pThreadList->pListData;
  PNODE                 pThreadNode = (PNODE) pThreadList->pCurrentNode;

  //-- free info data
  FreeThreadNodeInfo( pThreadNode );

  //-- delete and destroy node
  DeleteCurrentNode( pThreadList );

  //-- decrement dwActiveThreads
  pThreadListInfo->dwActiveThreads--;

  return( TRUE );
}

// ------------------------------------------------------------------------
// DLL list and node specific linked list wrapper functions
// ------------------------------------------------------------------------

// ************************************************************************
// FUNCTION : DllOrderFunction( PNODE, PNODE );
// PURPOSE  : Provides the sorting/search logic for the double linked
//            list package.
// COMMENTS :
//   Sorted by base address of the DLL
// ************************************************************************
int
DllOrderFunction( PNODE pNode1, PNODE pNode2 )
{
  PDEB_DLL_NODE_INFO pDllNodeInfo1 = pNode1->pNodeData;
  PDEB_DLL_NODE_INFO pDllNodeInfo2 = pNode2->pNodeData;

  if( pDllNodeInfo1->DllDebugInfo.lpBaseOfDll < pDllNodeInfo2->DllDebugInfo.lpBaseOfDll )
    return( LIST_LEFT_OF );

  if( pDllNodeInfo1->DllDebugInfo.lpBaseOfDll > pDllNodeInfo2->DllDebugInfo.lpBaseOfDll )
    return( LIST_RIGHT_OF );

  return( LIST_MATCH );
}

// ************************************************************************
// FUNCTION : CreateDllList( PLIST* )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
CreateDllList( PLIST* ppDllList )
{
  PDEB_DLL_LIST_INFO pDllListInfo;

  //-- create list
  CreateList( ppDllList, DllOrderFunction );

  //-- alloc info data
  pDllListInfo = (PDEB_DLL_LIST_INFO) HeapAlloc( hHeap, (DWORD) NULL,
                                                sizeof( DEB_DLL_LIST_INFO ) );
  (*ppDllList)->pListData = pDllListInfo;

  //-- init info data
  pDllListInfo->dwActiveDlls = 0;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DestroyDllList( PLIST )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
DestroyDllList( PLIST pDllList )
{
  PDEB_DLL_LIST_INFO pDllListInfo = pDllList->pListData;
  PNODE              pDeleteNode;

  //-- make sure all nodes are removed first
  while( pDllListInfo->dwActiveDlls ) {
    GetCurrentNode( pDllList, &pDeleteNode );
    DeleteCurrentDllNode( pDllList );
  }

  //-- free list data and destroy the list
  HeapFree( hHeap, (DWORD) NULL, (PVOID) pDllListInfo );
  DestroyList( pDllList );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : AllocDllNode( PNODE*, PDEB_DLL_NODE_INFO* )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
AllocDllNode( PNODE* ppDllNode, PDEB_DLL_NODE_INFO* ppDllNodeInfo )
{
  //-- create node
  CreateNode( ppDllNode );

  //-- alloc info data
  *ppDllNodeInfo = (PDEB_DLL_NODE_INFO) HeapAlloc( hHeap, (DWORD) NULL,
                                      sizeof( DEB_DLL_NODE_INFO ) );
  (*ppDllNode)->pNodeData = *(ppDllNodeInfo);
  (*ppDllNodeInfo)->lpstrFileName = (LPTSTR) HeapAlloc( hHeap, (DWORD) NULL, (DWORD) MAX_PATH );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : InitDllNodeInfo( PDEB_DLL_NODE_INFO*, LPDEBUG_EVENT )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
InitDllNodeInfo( PDEB_DLL_NODE_INFO* ppDllNodeInfo,
  LPDEBUG_EVENT lpDebugEvent )
{
  //-- init info data
  GetDllFileName( lpDebugEvent, (*ppDllNodeInfo)->lpstrFileName, MAX_PATH );
  (*ppDllNodeInfo)->DllDebugInfo = lpDebugEvent->u.LoadDll;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : InsertDllNode( PLIST, PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
InsertDllNode( PLIST pDllList, PNODE pDllNode )
{
  PDEB_DLL_LIST_INFO pDllListInfo = pDllList->pListData;

  // insert the node
  InsertNode( pDllList, pDllNode );

  //-- increment dwActiveDlls
  pDllListInfo->dwActiveDlls++;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : SetCurrentDllNode( PLIST, PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
SetCurrentDllNode( PLIST pDllList, PNODE pDllNode )
{
  SetCurrentNode( pDllList, pDllNode, pDllList->OrderFunction );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DeleteDllNode( PLIST, PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
DeleteDllNode( PLIST pDllList, PNODE pDllNode )
{
  PNODE pDeleteNode;

  SetCurrentNode( pDllList, pDllNode, pDllList->OrderFunction );
  GetCurrentNode( pDllList, &pDeleteNode );
  DeleteCurrentDllNode( pDllList );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : FreeDllNodeInfo( PNODE )
// PURPOSE  :
// COMMENTS :
//
// ************************************************************************
BOOL
FreeDllNodeInfo( PNODE pDllNode )
{
  PDEB_DLL_NODE_INFO pDllNodeInfo = (PDEB_DLL_NODE_INFO) pDllNode->pNodeData;

  //-- free info data
  HeapFree( hHeap, (DWORD) NULL, (PVOID) pDllNodeInfo->lpstrFileName );
  HeapFree( hHeap, (DWORD) NULL, (PVOID) pDllNodeInfo );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DestroyDllNode( PNODE )
// PURPOSE  :
// COMMENTS : Frees all memory associated with the node.
// ************************************************************************
BOOL
DestroyDllNode( PNODE pDllNode )
{
  //-- free info data
  FreeDllNodeInfo( pDllNode );

  //-- destroy node
  DestroyNode( pDllNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DeleteCurrentDllNode( PLIST )
// PURPOSE  :
// COMMENTS : Deletes the current DLL node from the list and frees all
//            memory associated with it.
// ************************************************************************
BOOL
DeleteCurrentDllNode( PLIST pDllList )
{
  PDEB_DLL_LIST_INFO pDllListInfo = pDllList->pListData;
  PNODE              pDllNode = (PNODE) pDllList->pCurrentNode;

  //-- free info data
  FreeDllNodeInfo( pDllNode );

  //-- delete and destroy node
  DeleteCurrentNode( pDllList );

  //-- decrement dwActiveDlls
  pDllListInfo->dwActiveDlls--;

  return( TRUE );
}


DEBDEBUG.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#ifndef DEBDEBUG_H

 #define DEBDEBUG_H

 #define EXCEPTION_VDM_EVENT ( (LONG) 0x40000005L )

 //-- debuggee startup information structure
 typedef struct DEB_STARTUP_INFO_STRUCT* PDEB_STARTUP_INFO;
 typedef struct DEB_STARTUP_INFO_STRUCT {
           BOOL                fActive;
           DWORD               dwProcessId;
           LPTSTR              lpstrFileName;
           LPTSTR              lpstrPathName;
           HWND                hWndListBox;
           STARTUPINFO         StartupInfo;
           PROCESS_INFORMATION ProcessInfo;
         } DEB_STARTUP_INFO;

 //-- linked list debug list information structures
 typedef struct DEB_PROCESS_LIST_INFO_STRUCT* PDEB_PROCESS_LIST_INFO;
 typedef struct DEB_PROCESS_LIST_INFO_STRUCT {
           DWORD dwActiveProcesses;
         } DEB_PROCESS_LIST_INFO;

 typedef struct DEB_THREAD_LIST_INFO_STRUCT* PDEB_THREAD_LIST_INFO;
 typedef struct DEB_THREAD_LIST_INFO_STRUCT {
           DWORD dwActiveThreads;
         } DEB_THREAD_LIST_INFO;

 typedef struct DEB_DLL_LIST_INFO_STRUCT* PDEB_DLL_LIST_INFO;
 typedef struct DEB_DLL_LIST_INFO_STRUCT {
           DWORD dwActiveDlls;
         } DEB_DLL_LIST_INFO;

 //-- linked list debug node information structures
 typedef struct DEB_PROCESS_NODE_INFO_STRUCT* PDEB_PROCESS_NODE_INFO;
 typedef struct DEB_PROCESS_NODE_INFO_STRUCT {
           DWORD                     dwProcessId;  // sort key
           DWORD                     dwThreadId;   // first thread
           LPTSTR                    lpstrFileName;
           LPTSTR                    lpstrPathName;
           PLIST                     pThreadList;
           PLIST                     pDllList;
           CREATE_PROCESS_DEBUG_INFO ProcessDebugInfo;
         } DEB_PROCESS_NODE_INFO;

 typedef struct DEB_THREAD_NODE_INFO_STRUCT* PDEB_THREAD_NODE_INFO;
 typedef struct DEB_THREAD_NODE_INFO_STRUCT {
           DWORD                    dwProcessId;
           DWORD                    dwThreadId;  // sort key
           CREATE_THREAD_DEBUG_INFO ThreadDebugInfo;
         } DEB_THREAD_NODE_INFO;

 typedef struct DEB_DLL_NODE_INFO_STRUCT* PDEB_DLL_NODE_INFO;
 typedef struct DEB_DLL_NODE_INFO_STRUCT {
           LPTSTR              lpstrFileName;
           LPTSTR              lpstrPathName;
           LOAD_DLL_DEBUG_INFO DllDebugInfo; // sort key =  DllDebugInfo.lpBaseOfDll
         } DEB_DLL_NODE_INFO;

 //-- public function prototypes
 DWORD WINAPI DebugEventThread( PDEB_STARTUP_INFO );

#endif // DEBDEBUG_H


DEBMAIN.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// ************************************************************************
// MODULE    : DEBMain.C
// PURPOSE   : A Win32 Application demonstrating the Debug APIs
// FUNCTIONS :
//   WinMain()                 - application entry point
//   MainWndProc()             - processes messages
//   ProcessCommandsWndProc()  - processes WM_COMMAND messages
//   PreferencesDlgProc()      - processes messages for "Preferences" dialog box
//   AttachDlgProc()           - processes messages for "Attach" dialog box
//   AboutDlgProc()            - processes messages for "About" dialog box
//   NewListBoxWndProc()       - subclass procedure to prevent listbox moving
//   TimerProc()               - timer procedure for animated icon
// COMMENTS  :
//
// ************************************************************************
#define   STRICT               // enable strict typing
#include <Windows.H>           // required for all Windows applications
#include "StdLib.H"            // __argc, __argv

#include "LinkList.H"          // double linked list package (OBJ)
#include "DEBDebug.H"          // debugging support functions
#include "DEBMisc.H"           // misc support functions
#include "DEBMain.H"           // specific to this module

// global data
// ------------------------------------------------------------------------
GLOBAL  Global;                // various global data
PROFILE Profile;               // various profile data

// internal data
// ------------------------------------------------------------------------
#define TIMEOUT_ANIMATED_ICON 150L // animated icon timeout
static TCHAR szSourceFileName[] = TEXT(__FILE__);

// location of various files
// ------------------------------------------------------------------------
TCHAR   szPath[MAX_PATH];         // path where the running application
                                  //  resides
TCHAR   szExePathName[MAX_PATH];  // full pathname of the application
TCHAR   szHelpPathName[MAX_PATH]; // full pathname of the application's
                                  //  help file
TCHAR   szIniPathName[MAX_PATH];  // full pathname of the application's
                                  //  ini file
// internal function prototypes
// ------------------------------------------------------------------------
LRESULT CALLBACK MainWndProc            ( HWND, UINT, WPARAM, LPARAM );
LRESULT CALLBACK ProcessCommandsWndProc ( HWND, UINT, WPARAM, LPARAM );
LRESULT CALLBACK NewListBoxWndProc      ( HWND, UINT, WPARAM, LPARAM );
BOOL    CALLBACK PreferencesDlgProc     ( HWND, UINT, WPARAM, LPARAM );
BOOL    CALLBACK AttachDlgProc          ( HWND, UINT, WPARAM, LPARAM );
BOOL    CALLBACK AboutDlgProc           ( HWND, UINT, WPARAM, LPARAM );
VOID    CALLBACK TimerProc              ( HWND, UINT, UINT, DWORD );

// ************************************************************************
// FUNCTION : WinMain( HINSTANCE, HINSTANCE, LPSTR, INT )
// PURPOSE  : initialize the window, process the message dispatch loop
// COMMENTS :
//
// ************************************************************************
INT WINAPI
WinMain( HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine,
  INT nCmdShow )
{
  MSG      Msg;
  WNDCLASS WndClass;
  HACCEL   hAccel;
  OSVERSIONINFO osvi;

  LPCTSTR lpszIconName    = TEXT( "DebugIcon"  );
  LPCTSTR lpszMenuName    = TEXT( "DebugMenu"  );
  LPCTSTR lpszClassName   = TEXT( "DebugClass" );
  LPCTSTR lpszAccelName   = TEXT( "DebugAccel" );
  LPCTSTR lpszIniFileExt  = TEXT( "INI"        );
  LPCTSTR lpszHelpFileExt = TEXT( "HLP"        );

  Global.hInstance = hInstance;
  Global.dwActiveDebuggees = 0;

  //-- Load the "A Windows API Failed" resource string
  {
    TCHAR szApiFailed[] = TEXT( "A Windows API Failed" );

    if( !LoadString( Global.hInstance, IDS_API_FAILED_MSG,
          Global.szApiFailed, sizeof(Global.szApiFailed) ) ) {
      ErrorMessageBox( TEXT( "First LoadString()" ),
        szApiFailed, szSourceFileName, __LINE__ );
      lstrcpy( Global.szApiFailed, szApiFailed );
    }
  }

  //-- Load all other resource strings
  if( !LoadString( Global.hInstance, IDS_APPNAME,  Global.szAppName,
         sizeof(Global.szAppName) ) )
    ErrorMessageBox( TEXT("LoadString()"),
      Global.szApiFailed, szSourceFileName, __LINE__ );
  if( !LoadString( Global.hInstance, IDS_SHORT_APPNAME,
         Global.szShortAppName, sizeof(Global.szShortAppName) ) )
    ErrorMessageBox( TEXT("LoadString()"),
      Global.szApiFailed, szSourceFileName, __LINE__ );

  //-- if compiled for Win32 (Unicode) and not Win32s then display
  //    notice and terminate

  //
  // Detect platform and exit gracefully if unsupported platform.
  //

  osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  GetVersionEx (&osvi);
  if (osvi.dwPlatformId == VER_PLATFORM_WIN32s) {
    TCHAR szTitleBuffer[64];
    TCHAR szTextBuffer[256];

    if( !LoadString( Global.hInstance, IDS_WINDOWS_NT_REQUIRED_TITLE,
           szTitleBuffer, sizeof(szTitleBuffer) ) )
      ErrorMessageBox( TEXT("LoadString()"),
        Global.szApiFailed, szSourceFileName, __LINE__ );

    if( !LoadString( Global.hInstance, IDS_WINDOWS_NT_REQUIRED,
           szTextBuffer, sizeof(szTextBuffer) ) )
      ErrorMessageBox( TEXT("LoadString()"),
        Global.szApiFailed, szSourceFileName, __LINE__ );

    MessageBox( NULL, szTextBuffer, szTitleBuffer,
      MB_APPLMODAL | MB_ICONSTOP | MB_OK );
    return( -1 );
  }

  //-- register the debug event window class
  WndClass.style         = CS_DBLCLKS;
  WndClass.lpfnWndProc   = (WNDPROC) MainWndProc;
  WndClass.cbClsExtra    = (INT) NULL;
  WndClass.cbWndExtra    = (INT) NULL;
  WndClass.hInstance     = Global.hInstance;
  WndClass.hIcon         = LoadIcon( Global.hInstance, lpszIconName );
  WndClass.hCursor       = LoadCursor( NULL, (LPTSTR) IDC_ARROW );
  WndClass.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE+1);
  WndClass.lpszMenuName  = lpszMenuName;
  WndClass.lpszClassName = lpszClassName;

  if( !RegisterClass(&WndClass) ) {
    ErrorMessageBox( TEXT("RegisterClass()"),
      Global.szApiFailed, szSourceFileName, __LINE__ );
    return( FALSE );
  }

  //-- get application pathname and store the ini and help file pathname
  //   (which is located in the same directory as the application)
  GetModuleFileName( (HANDLE) NULL, szExePathName,
    sizeof(szExePathName)/sizeof(TCHAR) );
  GetPathFromFullPathName( szExePathName, szPath,
    sizeof(szPath)/sizeof(TCHAR) );
  wsprintf( szIniPathName,  TEXT( "%s\\%s.%s" ), szPath,
    Global.szShortAppName, lpszIniFileExt  );
  wsprintf( szHelpPathName, TEXT( "%s\\%s.%s" ), szPath,
    Global.szShortAppName, lpszHelpFileExt );

  //-- retrieve stored default location from private profile data
  GetPrivateProfileSettings( Global.szAppName, szIniPathName, &Profile );

  //-- Create a main window for this application instance
  Global.hWndMain = CreateWindow( lpszClassName, Global.szAppName,
                      WS_OVERLAPPEDWINDOW,
                      Profile.xPos, Profile.yPos,
                      Profile.nWidth, Profile.nHeight,
                      NULL, NULL, Global.hInstance, NULL );

  //-- If window could not be created, return "failure"
  if( !Global.hWndMain ) {
    ErrorMessageBox( TEXT("CreateWindow()"),
      Global.szApiFailed, szSourceFileName, __LINE__ );
    return( FALSE );
  }

  //-- Load main menu accelerators
  if( !(hAccel = LoadAccelerators( Global.hInstance, lpszAccelName) ) ) {
    ErrorMessageBox( TEXT("LoadAccelerators()"),
      Global.szApiFailed, szSourceFileName, __LINE__ );
    return( FALSE );
  }

  //-- modify the menu to reflect saved settings
  UpdateMenuSettings( Global.hWndMain );

  //-- Make the window visible; update its client area; and return "success"
  if( Profile.fMaximized )
    ShowWindow( Global.hWndMain, SW_SHOWMAXIMIZED );
  else if ( Profile.fMinimized )
    ShowWindow( Global.hWndMain, SW_SHOWMINIMIZED );
  else
    ShowWindow( Global.hWndMain, SW_SHOWDEFAULT );
  UpdateWindow( Global.hWndMain );

  //-- Acquire and dispatch messages until a WM_QUIT message is received.
  while( GetMessage( &Msg, NULL, 0, 0 ) ) {
    if( !TranslateAccelerator( Global.hWndMain, hAccel, &Msg ) ) {
      TranslateMessage( &Msg );
      DispatchMessage( &Msg );
    }
  }

  return( Msg.wParam );
  UNREFERENCED_PARAMETER( lpCmdLine );  // avoid warnings
  UNREFERENCED_PARAMETER( hPrevInst );  // always NULL under Windows NT
  UNREFERENCED_PARAMETER( nCmdShow  );  //
}

// ************************************************************************
// FUNCTION : MainWndProc( HWND, UINT, WPARAM, LPARAM )
// PURPOSE  : Processes uMsgs
// MESSAGES :
//   WM_COMMAND   - passed to ProcessCommandsWndProc()
//   WM_DESTROY   - destroy window
//    ...
// COMMENTS :
//
// ************************************************************************
LRESULT CALLBACK
MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
  #define TOP_BORDER      4
  #define BOTTOM_BORDER   4
  #define SIDE_BORDER     4
  #define MIN_HEIGHT    128

  static HDC hDC;

  switch( uMsg ) {

    //-- forward all WM_COMMANDS to separate handler function
    case WM_COMMAND:
      return( ProcessCommandsWndProc( hWnd, uMsg, wParam, lParam) );

    //-- create debug event listbox
    case UM_CREATE_LISTBOX: {
      TCHAR  szWindowName[64];
      HFONT  hFont;

      LoadString( Global.hInstance, IDS_DEBUG_EVENTS, szWindowName,
        sizeof(szWindowName)/sizeof(TCHAR) );

      Global.hWndListBox = CreateWindow(
                           TEXT( "ListBox" ),
                           szWindowName,
                           WS_CHILD             | WS_VISIBLE      |
                           WS_CAPTION           | WS_VSCROLL      |
                           WS_HSCROLL           | LBS_NOTIFY      |
                           LBS_DISABLENOSCROLL  | LBS_USETABSTOPS |
                           LBS_NOINTEGRALHEIGHT,
                           (INT) SIDE_BORDER,
                           (INT) (Profile.fToolBar
                             ? (Global.ToolBarHeight + TOP_BORDER)
                             : TOP_BORDER),
                           (INT) Global.ListBoxSize.cx,
                           (INT) Global.ListBoxSize.cy,
                           hWnd, (HMENU) NULL, Global.hInstance, NULL );

      //-- Subclass the listbox so the user cannot move it
      Global.OldListBoxWndProc = SubclassWindow( Global.hWndListBox,
        (WNDPROC) NewListBoxWndProc );

      //-- set listbox font & background color
      hDC = GetDC( Global.hWndListBox );
      hFont = CreateFontIndirect( &(Profile.LogFont) );
      SelectObject( hDC, hFont );
      SendMessage( Global.hWndListBox, WM_CTLCOLORLISTBOX, (WPARAM) hDC,
        (LPARAM) Global.hWndListBox );
      SendMessage( Global.hWndListBox, WM_SETFONT, (WPARAM) hFont, TRUE );
      ReleaseDC( Global.hWndListBox, hDC );

      //-- if command line contains a debuggee name then
      //   start and detach the debug event processing thread
      if( __argc == 2 )
        StartDebuggee( (LPTSTR) __argv[1], Global.hWndListBox );

      return( FALSE );
    }

    //-- create ToolBar & send message to create the Debug Events listbox
    case WM_CREATE:
      Global.hWndToolBar = CreateTextButtonBar( hWnd, &Global.ToolBarHeight );
      if( Profile.fToolBar )
        ShowWindow( Global.hWndToolBar, SW_SHOW );
      PostMessage( hWnd, UM_CREATE_LISTBOX, 0, 0 );
      return( FALSE );

    //-- resize the debug event listbox when the window size changes
    case WM_SIZE:
      Global.ClientSize.cx = LOWORD( lParam );
      Global.ClientSize.cy = HIWORD( lParam );
      Global.ListBoxSize.cx = Global.ClientSize.cx - ( 2*SIDE_BORDER );
      Global.ListBoxSize.cy = max( Global.ClientSize.cy, MIN_HEIGHT
                                + TOP_BORDER )
        - (TOP_BORDER + BOTTOM_BORDER);

      if( Profile.fToolBar )
        Global.ListBoxSize.cy -= Global.ToolBarHeight;

      if( Global.hWndListBox != NULL)
        MoveWindow( Global.hWndListBox,
          (INT) SIDE_BORDER,
          (INT) (Profile.fToolBar ? Global.ToolBarHeight + TOP_BORDER : TOP_BORDER),
          (INT) Global.ListBoxSize.cx, (INT) Global.ListBoxSize.cy, TRUE );

      switch( wParam ) {

        case SIZE_RESTORED: {
          RECT rect;

          Profile.fMaximized = FALSE;
          Profile.fMinimized = FALSE;
          GetWindowRect( Global.hWndMain, &rect );
          Profile.nWidth  = (INT) (rect.right - rect.left);
          Profile.nHeight = (INT) (rect.bottom - rect.top);
          return( FALSE );
        }

        case SIZE_MAXIMIZED:
          Profile.fMaximized = TRUE;
          Profile.fMinimized = FALSE;
          Profile.xPos       = Global.xPosOld;
          Profile.yPos       = Global.yPosOld;
          return( FALSE );

        case SIZE_MINIMIZED:
          Profile.fMinimized = TRUE;
          Profile.fMaximized = FALSE;
          Profile.xPos       = Global.xPosOld;
          Profile.yPos       = Global.yPosOld;
          return( FALSE );

      }
      return( DefWindowProc(hWnd, uMsg, wParam, lParam) );

    //-- keep track of window position so it can be saved
    case WM_MOVE: {
      RECT rect;

      GetWindowRect( Global.hWndMain, &rect );
      Global.xPosOld      = Profile.xPos;
      Global.yPosOld      = Profile.yPos;
      Profile.xPos = (INT) rect.left;
      Profile.yPos = (INT) rect.top;
      return( FALSE );
    }

    //-- colorize the debug event listbox
    case WM_CTLCOLORLISTBOX: {
      LOGBRUSH LogBrush;

      LogBrush.lbStyle = BS_SOLID;
      LogBrush.lbColor = Profile.rgbBackColor;
      LogBrush.lbHatch = (LONG) NULL;

      SetTextColor( (HDC) wParam, Profile.rgbForeColor );
      SetBkColor( (HDC) wParam, Profile.rgbBackColor );

      return( (LPARAM) CreateBrushIndirect( &LogBrush ) );
    }

    //-- check if a debuggee is still active, save profile settings
    case WM_CLOSE:
      if( Global.dwActiveDebuggees ) {
        TCHAR  szExitBoxTitle[64];
        TCHAR  szExitBoxText[256];

        LoadString( Global.hInstance, IDS_EXIT_BOX_TITLE, szExitBoxTitle,
          sizeof(szExitBoxTitle)/sizeof(TCHAR) );
        LoadString( Global.hInstance, IDS_EXIT_BOX_TEXT, szExitBoxText,
          sizeof(szExitBoxText)/sizeof(TCHAR) );
        if ( MessageBox( hWnd, szExitBoxText, szExitBoxTitle,
               MB_YESNO | MB_ICONEXCLAMATION ) == IDNO )
          return( FALSE );
      }

      //-- store location information to private profile data
      WritePrivateProfileSettings( Global.szAppName, szIniPathName, &Profile );

      DestroyWindow( Global.hWndToolBar );
      DestroyWindow( Global.hWndListBox );
      DestroyWindow( Global.hWndMain );

      return( FALSE );

    case WM_DESTROY:
      if( Global.fHelpUsed )
        WinHelp( hWnd, szHelpPathName, (UINT) HELP_QUIT, (DWORD) NULL );
      PostQuitMessage( 0 );
      return( FALSE );

    default:               // Passes it on if unproccessed
      return( DefWindowProc(hWnd, uMsg, wParam, lParam) );
  }

  return( FALSE );
}

// ************************************************************************
// FUNCTION : ProcessCommandsWndProc( HWND, UINT, WPARAM, LPARAM )
// PURPOSE  : Processes WM_COMMAND messages for MainWndProc()
// MESSAGES :
//   WM_COMMAND         - application menu
//     IDM_FILE_EXIT    - exit the application
//     IDM_FILE_ABOUT   - About Dialog Box
//    ...
// COMMENTS :
//
// ************************************************************************
LRESULT CALLBACK
ProcessCommandsWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
  static LPCTSTR  lpszAboutDlgBox       = TEXT( "AboutDlgBox"       );
  static LPCTSTR  lpszAttachDlgBox      = TEXT( "AttachDlgBox"      );
  static LPCTSTR  lpszPreferencesDlgBox = TEXT( "PreferencesDlgBox" );

  switch( LOWORD(wParam) ) {

    //-- user requests to open a new debuggee
    case IDM_FILE_OPEN: {
      static TCHAR szDebuggeeFileName[MAX_PATH];

      if( Global.dwActiveDebuggees ) {
        MaxDebuggeesMessageBox( Global.hWndMain );
        return( FALSE );
      }
      if( !GetDebuggeeFileName( szDebuggeeFileName, hWnd ) ) {
        return( FALSE );
      }
      else {
        if( Profile.fClearOnNew ) {
          SendMessage( Global.hWndListBox, LB_RESETCONTENT, 0, 0 );
          Global.MaxStrLen = 0;
        }
        StartDebuggee( szDebuggeeFileName, Global.hWndListBox );
      }
      return( FALSE );
    }

    //-- user requests to attach to an existing process
    case IDM_FILE_ATTACH:
      if( Global.dwActiveDebuggees ) {
        MaxDebuggeesMessageBox( Global.hWndMain );
        return( FALSE );
      }
      if( !DialogBox( Global.hInstance, lpszAttachDlgBox, hWnd,
            (DLGPROC) AttachDlgProc ) ) {
        // handle cancel condition...
      }
      else {
        if( Profile.fClearOnNew ) {
          SendMessage( Global.hWndListBox, LB_RESETCONTENT, 0, 0 );
          Global.MaxStrLen = 0;
        }
      }
      return( FALSE );

    //-- copy listbox contents to the clipboard and clear the listbox
    case IDM_EDIT_CUT:
      CopyListBoxToClipboard( Global.hWndListBox, Global.MaxStrLen );
      SendMessage( hWnd, WM_COMMAND, IDM_EDIT_DELETE, 0 );
      return( FALSE );

    //-- copy listbox contents to the clipboard
    case IDM_EDIT_COPY: {
      CopyListBoxToClipboard( Global.hWndListBox, Global.MaxStrLen );
      return( FALSE );
    }

    //-- clear the contents of the listbox
    case IDM_EDIT_DELETE:
      SendMessage( Global.hWndListBox, LB_RESETCONTENT, 0, 0 );
      Global.MaxStrLen = 0;
      return( FALSE );

    //-- user requests a new font for the listbox
    case IDM_OPTIONS_FONT:
      if( !ChooseNewFont( Global.hWndListBox ) ) {
        // handle cancel condition...
      }
      return( FALSE );

    //-- user requests a new background color for the listbox
    case IDM_OPTIONS_COLOR:
      ChooseNewBackColor( Global.hWndListBox );
      return( FALSE );

    //-- invoke the preferences dialog box
    case IDM_OPTIONS_PREFERENCES:
      DialogBox( Global.hInstance, lpszPreferencesDlgBox, hWnd,
        (DLGPROC) PreferencesDlgProc );
      return( FALSE );

    //-- toggle the toolbar on or off
    case IDM_OPTIONS_TOOLBAR:
      if( Profile.fToolBar ) {
        Profile.fToolBar = 0;
        CheckMenuItem( GetMenu(Global.hWndMain), IDM_OPTIONS_TOOLBAR,
          MF_UNCHECKED );
        ShowWindow( Global.hWndToolBar, SW_HIDE );
        SendWmSizeMessage( Global.hWndMain );
      }
      else {
        Profile.fToolBar = 1;
        CheckMenuItem( GetMenu(Global.hWndMain), IDM_OPTIONS_TOOLBAR,
          MF_CHECKED );
        ShowWindow( Global.hWndToolBar, SW_SHOW );
        SendWmSizeMessage( Global.hWndMain );
      }
      return( FALSE );

    //-- toggles whether the used directory is used for the 'open' command
    case IDM_OPTIONS_SAVEDDIR:
      if( Profile.fSavedDirectory ) {
        Profile.fSavedDirectory = 0;
        CheckMenuItem( GetMenu(Global.hWndMain), IDM_OPTIONS_SAVEDDIR,
          MF_UNCHECKED );
      }
      else {
        Profile.fSavedDirectory = 1;
        CheckMenuItem( GetMenu(Global.hWndMain), IDM_OPTIONS_SAVEDDIR,
          MF_CHECKED );
      }
      return( FALSE );

    //-- toggles the 'save on exit' feature
    case IDM_OPTIONS_SAVEONEXIT:
      if( Profile.fSaveOnExit ) {
        Profile.fSaveOnExit = 0;
        CheckMenuItem( GetMenu(Global.hWndMain), IDM_OPTIONS_SAVEONEXIT,
          MF_UNCHECKED );
      }
      else {
        Profile.fSaveOnExit = 1;
        CheckMenuItem( GetMenu(Global.hWndMain), IDM_OPTIONS_SAVEONEXIT,
          MF_CHECKED );
      }
      return( FALSE );

    //-- store location information to private profile data now
    case IDM_OPTIONS_SAVENOW: {
      BOOL fOldSaveOnExit = Profile.fSaveOnExit;
      BOOL fOldSavePreferences = Profile.fSavePreferences;

      Profile.fSaveOnExit = TRUE;
      Profile.fSavePreferences = TRUE;
      WritePrivateProfileSettings( Global.szAppName, szIniPathName, &Profile );
      Profile.fSaveOnExit = fOldSaveOnExit;
      Profile.fSavePreferences = fOldSavePreferences;
      return( FALSE );
    }

    //-- invoke help and display the contents panel
    case IDM_HELP_CONTENTS:
      Global.fHelpUsed = TRUE;
      WinHelp( hWnd, (LPCTSTR) szHelpPathName, HELP_CONTENTS, (DWORD) NULL );
      return( FALSE );

    //-- search the help indexes
    case IDM_HELP_SEARCH:
      Global.fHelpUsed = TRUE;
      WinHelp( hWnd, (LPCTSTR) szHelpPathName, HELP_PARTIALKEY,
        (DWORD) TEXT( "" ) );
      return( FALSE );

    //-- invoke the main 'how to use' help panel
    case IDM_HELP_HOWTOUSE:
      Global.fHelpUsed = TRUE;
      WinHelp( hWnd, (LPTSTR) NULL, HELP_HELPONHELP, (DWORD) NULL );
      return( FALSE );

    //-- display the product information dialog box
    case IDM_HELP_ABOUT:
      DialogBox( Global.hInstance, lpszAboutDlgBox, hWnd,
        (DLGPROC) AboutDlgProc );
      return( FALSE );

    //-- the usr requests to terminate the app
    case IDM_FILE_EXIT:
      SendMessage( Global.hWndMain, WM_CLOSE, 0, 0 );
      return( FALSE );

    default:
      return( DefWindowProc(hWnd, uMsg, wParam, lParam) );
  }

  return( FALSE );
}

// ************************************************************************
// FUNCTION : PreferencesDlgProc( HWND, UINT, WPARAM, LPARAM )
// PURPOSE  : Processes message for "Preferences" dialog box
// MESSAGES :
//   WM_INITDIALOG - initialize dialog box
//   WM_COMMAND    - Input received
// COMMENTS :
//   Wait for user to click on "Ok" button, then close the dialog box.
// ************************************************************************
BOOL CALLBACK
PreferencesDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
  switch( uMsg ) {

    case WM_COMMAND:
      switch( LOWORD(wParam) ) {

        case IDOK:

          //-- Debugger Setting Group
          if( SendMessage( GetDlgItem( hDlg, IDC_DEBUG_PROCESS),
                BM_GETCHECK, 0, 0 ) )
            Profile.DebugMode = DEBUG_PROCESS;
          if( SendMessage( GetDlgItem( hDlg, IDC_DEBUG_ONLY_THIS_PROCESS),
                BM_GETCHECK, 0, 0 ) )
            Profile.DebugMode = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS;

          //-- Debuggee Priority Group
          if( SendMessage( GetDlgItem( hDlg, IDC_IDLE_PRIORITY_CLASS),
                BM_GETCHECK, 0, 0 ) )
            Profile.DebuggeePriority = IDLE_PRIORITY_CLASS;
          if( SendMessage( GetDlgItem( hDlg, IDC_NORMAL_PRIORITY_CLASS),
                BM_GETCHECK, 0, 0 ) )
            Profile.DebuggeePriority = NORMAL_PRIORITY_CLASS;
          if( SendMessage( GetDlgItem( hDlg, IDC_HIGH_PRIORITY_CLASS),
                BM_GETCHECK, 0, 0 ) )
            Profile.DebuggeePriority = HIGH_PRIORITY_CLASS;
          if( SendMessage( GetDlgItem( hDlg, IDC_REALTIME_PRIORITY_CLASS),
                BM_GETCHECK, 0, 0 ) )
            Profile.DebuggeePriority = REALTIME_PRIORITY_CLASS;

          //-- Miscellaneous Options Group
          Profile.fClearOnNew  = (BOOL) SendMessage(
                                          GetDlgItem( hDlg, IDC_CLEAR_ON_NEW),
                                          BM_GETCHECK, 0 , 0 );
          Profile.fVerbose     = (BOOL) SendMessage(
                                          GetDlgItem( hDlg, IDC_VERBOSE),
                                          BM_GETCHECK, 0 , 0 );

#ifdef SHOW_SYMBOLS
          Profile.fShowSymbols = (BOOL) SendMessage(
                                          GetDlgItem( hDlg, IDC_SHOW_SYMBOLS),
                                          BM_GETCHECK, 0 , 0 );
#endif

          //- Debug Error Level Group
          if( SendMessage( GetDlgItem( hDlg, IDC_DEBUG_MODE_NONE),
                BM_GETCHECK, 0, 0 ) )
            Profile.DebugErrorLevel = 0;
          if( SendMessage( GetDlgItem( hDlg, IDC_DEBUG_MODE_ERROR),
                BM_GETCHECK, 0, 0 ) )
            Profile.DebugErrorLevel = SLE_ERROR;
          if( SendMessage( GetDlgItem( hDlg, IDC_DEBUG_MODE_MINORERROR),
                BM_GETCHECK, 0, 0 ) )
            Profile.DebugErrorLevel = SLE_MINORERROR;
          if( SendMessage( GetDlgItem( hDlg, IDC_DEBUG_MODE_WARNING),
                BM_GETCHECK, 0, 0 ) )
            Profile.DebugErrorLevel = SLE_WARNING;

          Profile.fSavePreferences = (BOOL) SendMessage(
                                              GetDlgItem( hDlg,
                                                IDC_SAVE_PREFERENCES),
                                              BM_GETCHECK, 0 , 0 );
          EndDialog( hDlg, TRUE );
          return( TRUE );

        case IDCANCEL:
          EndDialog( hDlg, FALSE );
          return( TRUE );

        case IDHELP:
          return( TRUE );

      }
      break;

    case WM_INITDIALOG:

      //-- Debugger Setting Group
      switch( Profile.DebugMode ) {

        case DEBUG_PROCESS:
          SendMessage( GetDlgItem( hDlg, IDC_DEBUG_PROCESS),
            BM_SETCHECK, 1, 0);
          break;

        case ( DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS ):
          SendMessage( GetDlgItem( hDlg, IDC_DEBUG_ONLY_THIS_PROCESS),
            BM_SETCHECK, 1, 0);
          break;
      }

      //-- Debuggee Priority Group
      switch( Profile.DebuggeePriority ) {

        case IDLE_PRIORITY_CLASS:
          SendMessage( GetDlgItem( hDlg, IDC_IDLE_PRIORITY_CLASS),
            BM_SETCHECK, 1, 0);
          break;

        case NORMAL_PRIORITY_CLASS:
          SendMessage( GetDlgItem( hDlg, IDC_NORMAL_PRIORITY_CLASS),
            BM_SETCHECK, 1, 0);
          break;

        case HIGH_PRIORITY_CLASS:
          SendMessage( GetDlgItem( hDlg, IDC_HIGH_PRIORITY_CLASS),
            BM_SETCHECK, 1, 0);
          break;

        case REALTIME_PRIORITY_CLASS:
          SendMessage( GetDlgItem( hDlg, IDC_REALTIME_PRIORITY_CLASS),
            BM_SETCHECK, 1, 0);
          break;

      }

      //-- Miscellaneous Options Group
      SendMessage( GetDlgItem( hDlg, IDC_CLEAR_ON_NEW),     BM_SETCHECK,
        Profile.fClearOnNew, 0 );
      SendMessage( GetDlgItem( hDlg, IDC_VERBOSE),          BM_SETCHECK,
        Profile.fVerbose, 0 );
      SendMessage( GetDlgItem( hDlg, IDC_SAVE_PREFERENCES), BM_SETCHECK,
        Profile.fSavePreferences, 0 );

#ifdef SHOW_SYMBOLS
      SendMessage( GetDlgItem( hDlg, IDC_SHOW_SYMBOLS),     BM_SETCHECK,
        Profile.fShowSymbols, 0 );
#endif

      //- Debug Error Level Group
      switch( Profile.DebugErrorLevel ) {

        case 0:
          SendMessage( GetDlgItem( hDlg, IDC_DEBUG_MODE_NONE),
            BM_SETCHECK, 1, 0);
          break;

        case SLE_ERROR:
          SendMessage( GetDlgItem( hDlg, IDC_DEBUG_MODE_ERROR),
            BM_SETCHECK, 1, 0);
          break;

        case SLE_MINORERROR:
          SendMessage( GetDlgItem( hDlg, IDC_DEBUG_MODE_MINORERROR),
            BM_SETCHECK, 1, 0);
          break;

        case SLE_WARNING:
          SendMessage( GetDlgItem( hDlg, IDC_DEBUG_MODE_WARNING),
            BM_SETCHECK, 1, 0);
          break;

      }

      return( TRUE );
  }

  return( FALSE );
  UNREFERENCED_PARAMETER( lParam );
}

// ************************************************************************
// FUNCTION : AttachDlgProc( HWND, UINT, WPARAM, LPARAM )
// PURPOSE  : Processes messages for "Attach" dialog box
// MESSAGES :
//   WM_COMMAND    - Input received
//   WM_INITDIALOG - initialize dialog box
// COMMENTS :
//   Wait for user to click on "Ok" button, then close the dialog box.
// ************************************************************************
BOOL CALLBACK
AttachDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
  static HWND hWndProcessList;

  switch( uMsg ) {

    case WM_COMMAND:
      switch( LOWORD(wParam) ) {

        case IDOK: {
          LONG  Index;
          DWORD dwProcessId;

          Index = (UINT) SendMessage( hWndProcessList, LB_GETCURSEL,
                         (WPARAM) NULL, (LPARAM) NULL );
          dwProcessId = (DWORD) SendMessage( hWndProcessList,
                                  LB_GETITEMDATA,
                                  (WPARAM) Index, (LPARAM) NULL );
          AttachToDebuggee( dwProcessId, Global.hWndListBox );

EndDialog( hDlg, TRUE );
          return( TRUE );
        }

        case IDCANCEL:
          EndDialog( hDlg, FALSE );
          return( TRUE );

        case IDHELP:
          return( TRUE );

      }
      switch( HIWORD( wParam ) ) {
        case LBN_DBLCLK:
          SendMessage( hDlg, WM_COMMAND, (WPARAM) IDOK, (LPARAM) 0L );
          return( TRUE );
      }
      break;

    case WM_INITDIALOG:
      hWndProcessList = GetDlgItem( hDlg, IDC_PROCESSLIST );
      SendMessage( hWndProcessList, LB_RESETCONTENT, 0 , 0 );
      for(; !EnumWindows( (WNDENUMPROC) EnumProcessListFunc,
               (LPARAM) hWndProcessList ); )
        ; // continue looping until done
      return( TRUE );
  }

  return( FALSE );
  UNREFERENCED_PARAMETER( lParam );
}

// **************************************************************************
// FUNCTION : AboutDlgProc( HWND, UINT, WPARAM, LPARAM )
// PURPOSE  : Processes messages for "About" dialog box
// MESSAGES :
//   WM_COMMAND    - Input received
//     IDOK        - OK button selected
//     IDCANCEL    - Cancel button selected
//     ...
//   WM_INITDIALOG - initialize dialog box
//   WM_CLOSE      - close the dialog box
//   ...
// COMMENTS:
//   No initialization is needed for this particular dialog box.
//   In this case, TRUE must be returned to Windows.
//   Wait for user to click on "Ok" button, then close the dialog box.
// **************************************************************************
BOOL CALLBACK
AboutDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
  switch( uMsg ) {

    case WM_COMMAND:
      switch( LOWORD(wParam) ) {

        case IDOK:
          EndDialog( hDlg, TRUE );
          return( TRUE );

        case IDCANCEL:
          EndDialog( hDlg, FALSE );
          return( FALSE );

      }
      break;

    case WM_INITDIALOG:
      SetTimer( hDlg, 1, TIMEOUT_ANIMATED_ICON, TimerProc );
      return( TRUE );

    case WM_CLOSE:
      EndDialog( hDlg, TRUE );
      return( TRUE );

    case WM_DESTROY:
      KillTimer( hDlg, 1 );
      return( TRUE );
  }

  return( FALSE );
  UNREFERENCED_PARAMETER( lParam );
}

// ************************************************************************
// FUNCTION : NewListBoxWndProc( HWND, UINT, WPARAM, LPARAM )
// PURPOSE  : Processes messages for "LISTBOX" class.
// COMMENTS : Prevents the user from moving the window
//            by dragging the titlebar.
// ************************************************************************
LRESULT CALLBACK
NewListBoxWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
  switch( uMsg ) {

    case WM_NCLBUTTONDOWN:
      if( wParam == HTCAPTION ) {
        SetFocus( hWnd );
        return( FALSE );
      }
      else
        break;
  }

  return( CallWindowProc( Global.OldListBoxWndProc, hWnd, uMsg, wParam, lParam) );
}

// ************************************************************************
// FUNCTION : TimerProc( HWND, UINT, UINT, DWORD )
// PURPOSE  : Timer callback fuction that changes the dialog's icon (thus
//            a animated icon) for each timer message received.
// COMMENTS :
// ************************************************************************
VOID CALLBACK
TimerProc( HWND hDlg, UINT uMsg, UINT idEvent, DWORD dwTime )
{
 #define NUM_ICONS 8

 static UINT    uIconNumber = 0;
 static HWND    hWndIcon    = NULL;
 static HWND    hWndIconOld = NULL;
 static LPCTSTR lpszIconNames[NUM_ICONS] = {
                  TEXT( "DebugIcon1" ), TEXT( "DebugIcon2" ),
                  TEXT( "DebugIcon3" ), TEXT( "DebugIcon4" ),
                  TEXT( "DebugIcon5" ), TEXT( "DebugIcon6" ),
                  TEXT( "DebugIcon7" ), TEXT( "DebugIcon8" ) };

 if( (++uIconNumber) >= NUM_ICONS )
   uIconNumber = 0;

 hWndIconOld = hWndIcon;
 hWndIcon = CreateIconWindow( hDlg, lpszIconNames[uIconNumber] );
 if( hWndIcon != NULL )
   DestroyWindow( hWndIconOld );

 return;
 UNREFERENCED_PARAMETER( uMsg );
 UNREFERENCED_PARAMETER( idEvent );
 UNREFERENCED_PARAMETER( dwTime );
}


DEBMAIN.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#ifndef DEBMAIN_H

 #define DEBMAIN_H

 #ifndef DEB_H
  #include "DEB.H"    // resource and message ids
 #endif

 // ********
 // typedefs
 // ********
 typedef struct PROFILE_STRUCT {
   INT       xPos;               // application window's horizontal position
   INT       yPos;               // application window's vertical position
   INT       nWidth;             // application window's width
   INT       nHeight;            // application window's height
   BOOL      fMaximized;         // flag: maximized state?
   BOOL      fMinimized;         // flag: minimized state?
   BOOL      fToolBar;           // flag: display the ToolBar?
   BOOL      fSavedDirectory;    // flag: use saved directory for opening files?
   TCHAR     szInitialDir[MAX_PATH];  // initial directory for debuggees
   LOGFONT   LogFont;            // current logical font
   COLORREF  rgbForeColor;       // current listbox text foreground color
   COLORREF  rgbBackColor;       // current listbox text foreground color
   BOOL      fClearOnNew;        // flag: clears the event listbox on a new debuggee
   BOOL      fVerbose;           // flag: verbose event listings
   BOOL      fShowSymbols;       // flag: show symbols (limited)
   LONG      DebugMode;          // debug mode flag CreateProcess
   LONG      DebuggeePriority;   // debuggee priority CreateProcess
   DWORD     DebugErrorLevel;    // debugger error level
   BOOL      fSavePreferences;   // flag: save the preferences settings
   BOOL      fSaveOnExit;        // flag: save settings upon exit?
 } PROFILE, *PPROFILE;

 typedef struct GLOBAL_STRUCT {
   HINSTANCE hInstance;          // current instance
   HWND      hWndMain;           // Main window handle
   HWND      hWndListBox;        // debug event listbox handle
   HWND      hWndToolBar;        // windows handle to the text toolbar
   WNDPROC   OldListBoxWndProc;  // original listbox procedure
   TCHAR     szAppName[64];      // name of the application
   TCHAR     szShortAppName[32]; // short name of the application
   TCHAR     szApiFailed[64]; // "A Windows API Failed" message
   LONG      MaxStrLen;          // maximum string length in Debug Event listbox
   DWORD     dwActiveDebuggees;  // count of active debuggee processes
   SIZE      ClientSize;         // client windows size
   SIZE      ListBoxSize;        // debug event listbox size
   INT       ToolBarHeight;      // text button bar height
   INT       xPosOld;            // application window's old horizontal pos
   INT       yPosOld;            // application window's old vertical pos
   BOOL      fHelpUsed;          // boolean denoting online help invoked
 } GLOBAL, *PGLOBAL;

 // ***********
 // global data
 // ***********
 extern GLOBAL  Global;          // various global data
 extern PROFILE Profile;         // various profile data

#endif // DEBMAIN_H


DEBMISC.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// ************************************************************************
// MODULE    : DEBMisc.C
// PURPOSE   : Miscellaneous support functions for the Debug Event Browser
// FUNCTIONS :
//   StartDebuggee()               - starts a new debuggee process
//   AttachToDebuggee()            - attaches to an existing process
//   EnumProcessListFunc()         - enumeration func for active processes
//   GetDebuggeeFileName()         - get the name of a debugee file to open
//   ChooseNewFont()               - choose a new font
//   ChooseNewBackColor()          - choose a new background color
//   MakeCommonDebugEventString()  - create common debug event info string
//   CreateTextButtonBar()         - create a text button bar
//   CreateIconWindow()            - creates the icon window in the dialog
//   GetPrivateProfileSettings()   - gets stored profile settings
//   WritePrivateProfileSettings() - stores default settings in profile
//   WritePrivateProfileInt()      - opposite of GetPrivateProfileInt()
//   UpdateMenuSettings()          - update menu check marks
//   OutOfMemoryMessageBox()       - displays an out of memory message box
//   MaxDebuggeesMessageBox()      - displays max debuggees exceeded
//   ErrorMessageBox()             - displays an error message box
//   SubclassWindow()              - generic window subclass func
//   SendWmSizeMessage()           - sends WM_SIZE with current size
//   GetPathFromFullPathName()     - extracts path from a pathname
//   CopyListBoxToClipboard()      - copies a listbox into the clipboard
//   ListBoxInsert()               - insert a string into a listbox
//   ListBoxPrintF()               - printf for listboxes
//   StringPrintF()                - formats a string buffer
//   StringAppendF()               - appends a formated string
// COMMENTS  :
//
// ************************************************************************
#define   STRICT               // enable strict typing
#include <Windows.H>           // required for all Windows applications
#include <CommDlg.H>           // GetOpenFileName(), ChooseFont(), etc.
#include <Dlgs.H>              // templates and defines for the common dialogs
#include <StdArg.H>            // va_list, va_start()

#include "LinkList.H"          //
#include "ToolBar.H"           //
#include "DEBMisc.H"           //

// ************************************************************************
// FUNCTION : StartDebuggee( LPTSTR, HWND )
// PURPOSE  : starts a new debuggee process given a filename
// COMMENTS :
//   Return TRUE on success else FALSE.
// ************************************************************************
BOOL
StartDebuggee( LPTSTR lpszDebuggeeFileName, HWND hWndListBox )
{
  static PDEB_STARTUP_INFO pDebStartupInfo;
  static BOOL              fFirstTime = TRUE;

  static TCHAR szDebuggeeTitle[128];

  HANDLE hDebugEventThread;
  DWORD  idDebugEventThread;

  //-- Load resource strings and init the OpenFileName struct
  //   (do this only one time)
  if( fFirstTime ) {
    fFirstTime = FALSE;
    LoadString( NULL, IDS_OFN_DEBUGGEE_TITLE, szDebuggeeTitle,
      sizeof(szDebuggeeTitle)/sizeof(TCHAR) );
  }

  //-- allocate the Debuggee Information structure
  pDebStartupInfo = NULL;
  pDebStartupInfo = (PDEB_STARTUP_INFO) VirtualAlloc(
                                    pDebStartupInfo,
                                    sizeof( DEB_STARTUP_INFO ),
                                    MEM_RESERVE | MEM_COMMIT,
                                    PAGE_READWRITE );

  //-- init the StartupInfo struct
  (pDebStartupInfo->StartupInfo).cb          = sizeof( STARTUPINFO );
  (pDebStartupInfo->StartupInfo).lpDesktop   = NULL;
  (pDebStartupInfo->StartupInfo).lpTitle     = szDebuggeeTitle;
  (pDebStartupInfo->StartupInfo).dwX         = 0;
  (pDebStartupInfo->StartupInfo).dwY         = 0;
  (pDebStartupInfo->StartupInfo).dwXSize     = 0;
  (pDebStartupInfo->StartupInfo).dwYSize     = 0;
  (pDebStartupInfo->StartupInfo).dwFlags     = (DWORD) NULL;
  (pDebStartupInfo->StartupInfo).wShowWindow = SW_SHOWDEFAULT;

  (pDebStartupInfo->ProcessInfo).hProcess = NULL;

  //-- init other debuggee info
  pDebStartupInfo->fActive       = FALSE;
  pDebStartupInfo->dwProcessId   = (DWORD) NULL;
  pDebStartupInfo->lpstrFileName = NULL;
  pDebStartupInfo->lpstrPathName = lpszDebuggeeFileName;
  pDebStartupInfo->hWndListBox   = hWndListBox;

  //-- now start and detach the debug event processing thread
  if( !( hDebugEventThread = CreateThread(
                               (LPSECURITY_ATTRIBUTES) NULL,
                               (DWORD) 0,
                               (LPTHREAD_START_ROUTINE) DebugEventThread,
                               (LPVOID) pDebStartupInfo,
                               (DWORD) NULL,
                               (LPDWORD) &idDebugEventThread) ) ) {

    VirtualFree( pDebStartupInfo, 0, MEM_RELEASE );
    return( FALSE );
  }
  else{
    CloseHandle( hDebugEventThread );
  }

  return( TRUE );
}

// ************************************************************************
// FUNCTION : AttachToDebuggee( DWORD, HWND )
// PURPOSE  : attaches to a currently running process
// COMMENTS :
//   Return TRUE on success else FALSE.
// ************************************************************************
BOOL
AttachToDebuggee( DWORD dwProcessId, HWND hWndListBox )
{
  static PDEB_STARTUP_INFO pDebStartupInfo;
  static BOOL              fFirstTime = TRUE;

  static TCHAR szDebuggeeTitle[128];

  HANDLE hDebugEventThread;
  DWORD  idDebugEventThread;

  //-- Load resource strings and init the OpenFileName struct
  //   (do this only one time)
  if( fFirstTime ) {
    fFirstTime = FALSE;
    LoadString( NULL, IDS_OFN_DEBUGGEE_TITLE, szDebuggeeTitle,
      sizeof(szDebuggeeTitle)/sizeof(TCHAR) );
  }

  //-- allocate the Debuggee Information structure
  pDebStartupInfo = NULL;
  pDebStartupInfo = (PDEB_STARTUP_INFO) VirtualAlloc(
                                    pDebStartupInfo,
                                    sizeof( DEB_STARTUP_INFO ),
                                    MEM_RESERVE | MEM_COMMIT,
                                    PAGE_READWRITE );

  //-- init the StartupInfo struct
  (pDebStartupInfo->StartupInfo).cb          = sizeof( STARTUPINFO );
  (pDebStartupInfo->StartupInfo).lpDesktop   = NULL;
  (pDebStartupInfo->StartupInfo).lpTitle     = szDebuggeeTitle;
  (pDebStartupInfo->StartupInfo).dwX         = 0;
  (pDebStartupInfo->StartupInfo).dwY         = 0;
  (pDebStartupInfo->StartupInfo).dwXSize     = 0;
  (pDebStartupInfo->StartupInfo).dwYSize     = 0;
  (pDebStartupInfo->StartupInfo).dwFlags     = (DWORD) NULL;
  (pDebStartupInfo->StartupInfo).wShowWindow = SW_SHOWDEFAULT;

  (pDebStartupInfo->ProcessInfo).hProcess = NULL;

  //-- init other debuggee info
  pDebStartupInfo->fActive       = TRUE;
  pDebStartupInfo->dwProcessId   = dwProcessId;
  pDebStartupInfo->lpstrFileName = NULL;
  pDebStartupInfo->lpstrPathName = NULL;
  pDebStartupInfo->hWndListBox   = hWndListBox;

  //-- now start and detach the debug event processing thread
  if( !( hDebugEventThread = CreateThread(
                               (LPSECURITY_ATTRIBUTES) NULL,
                               (DWORD) 0,
                               (LPTHREAD_START_ROUTINE) DebugEventThread,
                               (LPVOID) pDebStartupInfo,
                               (DWORD) NULL,
                               (LPDWORD) &idDebugEventThread) ) ) {

    VirtualFree( pDebStartupInfo, 0, MEM_RELEASE );
    return( FALSE );
  }
  else{
    CloseHandle( hDebugEventThread );
  }

  return( TRUE );
}

// ************************************************************************
// FUNCTION : EnumProcessListFunc( HWND, LPARAM )
// PURPOSE  : Callback function for EnumWindows
// COMMENTS : Inserts found window title strings into the listbox.
//            Return !NULL to continue enumeration.
// ************************************************************************
BOOL CALLBACK
EnumProcessListFunc( HWND hWnd, LPARAM lParam )
{
  static DWORD dwCurrentProcessId;
  static BOOL  fFirstTime = TRUE;
  static LONG  MaxStrLen  = 0;
  static TCHAR TextBuffer[256];

  if( fFirstTime ) {
    fFirstTime = FALSE;
    dwCurrentProcessId = GetCurrentProcessId();
  }

  if( hWnd ) {
    GetWindowText( hWnd, (LPTSTR) &TextBuffer, sizeof(TextBuffer) );
    if( *TextBuffer ) {
      DWORD  dwProcessId;

      GetWindowThreadProcessId( hWnd, &dwProcessId );
      if( dwProcessId != dwCurrentProcessId ) {
        LONG Index;
        HWND hWndListBox = (HWND) lParam;

        Index = ListBoxInsert( hWndListBox, &MaxStrLen, TextBuffer );
        SendMessage( hWndListBox, LB_SETITEMDATA, (WPARAM) Index,
          (LPARAM) dwProcessId );
      }
    }
  }

  return( TRUE );
}

// ************************************************************************
// FUNCTION : GetDebuggeeFileName( LPTSTR, HWND )
// PURPOSE  : Get the name of a debugee file to open.
// COMMENTS :
//   Return TRUE on success else FALSE.
// ************************************************************************
BOOL
GetDebuggeeFileName( LPTSTR lpszDebuggeeFileName, HWND hWnd )
{
  static BOOL  fFirstTime = TRUE;
  static TCHAR szFilter[128]         = TEXT("");
  static TCHAR szTitle[64]           = TEXT("");
  static TCHAR szFileTitle[MAX_PATH] = TEXT("");
  static TCHAR szFile[MAX_PATH]      = TEXT("");
  static TCHAR szDefExt[8]           = TEXT("");
  static OPENFILENAME OpenFileName;

  //-- Load resource strings and init the OpenFileName struct
  //   (do this only one time)
  if( fFirstTime ) {
    static UINT FilterNameLen;

    fFirstTime = FALSE;
    FilterNameLen = (UINT) LoadString( NULL, IDS_OFN_FILTERNAME,
                             szFilter, sizeof(szFilter) );
    FilterNameLen++;
    LoadString( NULL, IDS_OFN_FILTER, &szFilter[FilterNameLen],
      sizeof(szFilter)-FilterNameLen );
    LoadString( NULL, IDS_OFN_TITLE, szTitle, sizeof(szTitle) );

    OpenFileName.lStructSize       = sizeof(OPENFILENAME);
    OpenFileName.hwndOwner         = hWnd;
    OpenFileName.hInstance         = (HANDLE) NULL;
    OpenFileName.lpstrFilter       = szFilter;
    OpenFileName.lpstrCustomFilter = NULL;
    OpenFileName.nMaxCustFilter    = (DWORD) NULL;
    OpenFileName.nFilterIndex      = 1L;
    OpenFileName.lpstrFile         = szFile;
    OpenFileName.nMaxFile          = sizeof(szFile);
    OpenFileName.lpstrFileTitle    = szFileTitle;
    OpenFileName.nMaxFileTitle     = sizeof(szFileTitle);
    OpenFileName.lpstrInitialDir   = (Profile.fSavedDirectory ? Profile.szInitialDir : NULL);
    OpenFileName.lpstrTitle        = (LPTSTR) szTitle;
    OpenFileName.Flags             = OFN_HIDEREADONLY;
    OpenFileName.nFileOffset       = (WORD) NULL;
    OpenFileName.nFileExtension    = (WORD) NULL;
    OpenFileName.lpstrDefExt       = szDefExt;
    OpenFileName.lCustData         = (DWORD) NULL;
    OpenFileName.lpfnHook          = (LPOFNHOOKPROC) NULL;
    OpenFileName.lpTemplateName    = (LPTSTR) NULL;
  }

  if( !GetOpenFileName( &OpenFileName ) ) {
    return( FALSE );
  }

  //-- store recent directory by stripping off the EXE name from the full path
  GetPathFromFullPathName( (LPTSTR) OpenFileName.lpstrFile, Profile.szInitialDir,
    sizeof(Profile.szInitialDir) );

  //-- copy name to return buffer
  lstrcpy( lpszDebuggeeFileName, OpenFileName.lpstrFile );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : ChooseFontHookProc(  )
// PURPOSE  : Disable the Effects group and its contents
// COMMENTS :
// ************************************************************************
LRESULT CALLBACK
ChooseFontHookProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
  UNREFERENCED_PARAMETER( lParam );
  UNREFERENCED_PARAMETER( wParam );

  switch( uMsg ) {

    case WM_INITDIALOG:
      ShowWindow( GetDlgItem(hDlg, grp1), SW_HIDE );
      ShowWindow( GetDlgItem(hDlg, chx1), SW_HIDE );
      ShowWindow( GetDlgItem(hDlg, chx2), SW_HIDE );
      break;

  }

  return( FALSE );
}

// ************************************************************************
// FUNCTION : ChooseNewFont( HWND )
// PURPOSE  : Choose a new font for a listbox
// COMMENTS : Return TRUE on success else FALSE
// ************************************************************************
BOOL
ChooseNewFont( HWND hWndListBox )
{
  static CHOOSEFONT ChooseFontStruct;
  static BOOL       fFirstTime = TRUE;
  HFONT             hFont;

  if( fFirstTime ) {
    fFirstTime = FALSE;

    ChooseFontStruct.lStructSize    = sizeof( CHOOSEFONT );
    ChooseFontStruct.hwndOwner      = hWndListBox;
    ChooseFontStruct.hDC            = (HDC) NULL;
    ChooseFontStruct.lpLogFont      = &(Profile.LogFont);
    ChooseFontStruct.iPointSize     = (INT) NULL;
    ChooseFontStruct.Flags          = CF_INITTOLOGFONTSTRUCT |
                                      CF_SCREENFONTS | CF_EFFECTS |
                                      CF_ENABLEHOOK;
    ChooseFontStruct.rgbColors      = Profile.rgbForeColor;
    ChooseFontStruct.lCustData      = (DWORD) NULL;
    ChooseFontStruct.lpfnHook       = (LPCFHOOKPROC) ChooseFontHookProc;
    ChooseFontStruct.lpTemplateName = (LPTSTR) NULL;
    ChooseFontStruct.hInstance      = (HANDLE) NULL;
    ChooseFontStruct.lpszStyle      = (LPTSTR) NULL;
    ChooseFontStruct.nFontType      = SCREEN_FONTTYPE;
    ChooseFontStruct.nSizeMin       = (INT) NULL;
    ChooseFontStruct.nSizeMax       = (INT) NULL;
  }

  if( ChooseFont( &ChooseFontStruct ) ) {
    HDC    hDC;

    hFont = CreateFontIndirect( &(Profile.LogFont) );
    hDC = GetDC( hWndListBox );
    SelectObject( hDC, hFont );
    Profile.rgbForeColor = ChooseFontStruct.rgbColors;
    InvalidateRect( hWndListBox, NULL, TRUE );
    SendMessage( hWndListBox, WM_CTLCOLORLISTBOX, (DWORD) hDC,
      (LONG) hWndListBox );
    SendMessage( hWndListBox, WM_SETFONT, (DWORD) hFont, TRUE );
    ReleaseDC( hWndListBox, hDC );
  }

  return( TRUE );
}

// ************************************************************************
// FUNCTION : ChooseNewBackColor( HWND )
// PURPOSE  : Choose the background color for a listbox
// COMMENTS : Return TRUE on success else FALSE
// ************************************************************************
BOOL
ChooseNewBackColor( HWND hWndListBox )
{
  static BOOL        fFirstTime = TRUE;
  static CHOOSECOLOR ChooseColorStruct;
  static DWORD       dwCustColors[16];

  if( fFirstTime ) {
    int i;

    fFirstTime = FALSE;

    //-- set the custom colors to white
    for( i = 0; i < 16; i++ )
      dwCustColors[i] = RGB(255,255,255);

    ChooseColorStruct.lStructSize    = sizeof( CHOOSECOLOR );
    ChooseColorStruct.hwndOwner      = hWndListBox;
    ChooseColorStruct.hInstance      = (HANDLE) NULL;
    ChooseColorStruct.rgbResult      = Profile.rgbBackColor;
    ChooseColorStruct.lpCustColors   = (LPDWORD) dwCustColors;
    ChooseColorStruct.Flags          = CC_RGBINIT;
    ChooseColorStruct.lCustData      = 0L;
    ChooseColorStruct.lpfnHook       = (LPCCHOOKPROC) NULL;
    ChooseColorStruct.lpTemplateName = (LPTSTR) NULL;
  }

  if( ChooseColor( &ChooseColorStruct ) ) {
    HDC    hDC;

    Profile.rgbBackColor = (COLORREF) ChooseColorStruct.rgbResult;
    hDC = GetDC( hWndListBox );
    SendMessage( hWndListBox, WM_CTLCOLORLISTBOX, (DWORD) hDC,
      (LONG) hWndListBox );
    ReleaseDC( hWndListBox, hDC );
    InvalidateRect( hWndListBox, NULL, TRUE );
  }

  return( TRUE );
}

// ************************************************************************
// FUNCTION : MakeCommonDebugEventString( LPTSTR, LPDEBUG_EVENT )
// PURPOSE  : Fill in the Debug Event information into a string buffer
// COMMENTS : The string buffer contains the information common to all
//            debug events ( PID, TID ).
// ************************************************************************
BOOL
MakeCommonDebugEventString( LPTSTR lpszBuffer, LPDEBUG_EVENT lpDebugEvent )
{
  StringPrintF( lpszBuffer, TEXT( "PID:0x%X \t TID:0x%X \t " ),
    lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : CreateTextButtonBar( HWND, LPINT )
// PURPOSE  : Creates a Text Button Bar from the resource strings.
// COMMENTS : Returns window handle to the created text button bar
// ************************************************************************
HWND
CreateTextButtonBar( HWND hWndParent, LPINT lpTextButtonBarHeight )
{
  TEXTBUTTON   TextButton[9];
  static TCHAR szButtonOpen[32];
  static TCHAR szButtonAttach[32];
  static TCHAR szButtonCut[32];
  static TCHAR szButtonCopy[32];
  static TCHAR szButtonDelete[32];
  static TCHAR szButtonHelp[32];

  LoadString( NULL, IDS_BUTTON_OPEN,   szButtonOpen,   sizeof(szButtonOpen)/sizeof(TCHAR)   );
  LoadString( NULL, IDS_BUTTON_ATTACH, szButtonAttach, sizeof(szButtonAttach)/sizeof(TCHAR) );
  LoadString( NULL, IDS_BUTTON_CUT,    szButtonCut,    sizeof(szButtonCut)/sizeof(TCHAR)    );
  LoadString( NULL, IDS_BUTTON_COPY,   szButtonCopy,   sizeof(szButtonCopy)/sizeof(TCHAR)   );
  LoadString( NULL, IDS_BUTTON_DELETE, szButtonDelete, sizeof(szButtonDelete)/sizeof(TCHAR) );
  LoadString( NULL, IDS_BUTTON_HELP,   szButtonHelp,   sizeof(szButtonHelp)/sizeof(TCHAR)   );

  TextButton[0].lpButtonText = szButtonOpen;
  TextButton[0].idButton     = IDM_FILE_OPEN;
  TextButton[0].hWndButton   = NULL;

  TextButton[1].lpButtonText = szButtonAttach;
  TextButton[1].idButton     = IDM_FILE_ATTACH;
  TextButton[1].hWndButton   = NULL;

  TextButton[2].lpButtonText = NULL;
  TextButton[2].idButton     = TB_SPACE;
  TextButton[2].hWndButton   = NULL;

  TextButton[3].lpButtonText = szButtonCut;
  TextButton[3].idButton     = IDM_EDIT_CUT;
  TextButton[3].hWndButton   = NULL;

  TextButton[4].lpButtonText = szButtonCopy;
  TextButton[4].idButton     = IDM_EDIT_COPY;
  TextButton[4].hWndButton   = NULL;

  TextButton[5].lpButtonText = szButtonDelete;
  TextButton[5].idButton     = IDM_EDIT_DELETE;
  TextButton[5].hWndButton   = NULL;

  TextButton[6].lpButtonText = NULL;
  TextButton[6].idButton     = TB_SPACE;
  TextButton[6].hWndButton   = NULL;

  TextButton[7].lpButtonText = szButtonHelp;
  TextButton[7].idButton     = IDM_HELP_CONTENTS;
  TextButton[7].hWndButton   = NULL;

  TextButton[8].lpButtonText = (LPTSTR) NULL;
  TextButton[8].idButton     = (INT)   NULL;
  TextButton[8].hWndButton   = (HWND)  NULL;

  return( TextButtonBar( hWndParent, TextButton, lpTextButtonBarHeight ) );
}

// ************************************************************************
// FUNCTION : CreateIconWindow( HWND, LPCTSTR )
// PURPOSE  :
// COMMENTS :
// ************************************************************************
HWND
CreateIconWindow( HWND hDlg, LPCTSTR lpstrIcon )
{
  return ( CreateWindow( TEXT( "static" ), lpstrIcon,
             WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SS_ICON,
             7, 5, 0, 0,
             hDlg, NULL, GetModuleHandle( NULL ), NULL ) );
}

// ************************************************************************
// FUNCTION : GetPrivateProfileSettings( LPCTSTR, LPCTSTR, PPROFILE )
// PURPOSE  : Retrieves the stored preferences and options from the
//            profile (registry)
// COMMENTS :
// ************************************************************************
BOOL
GetPrivateProfileSettings( LPCTSTR lpszAppTitle, LPCTSTR lpszIniPathName,
  PPROFILE pProfile )
{
  pProfile->xPos       = (INT) GetPrivateProfileInt(  lpszAppTitle, TEXT( "xPos" ),       CW_USEDEFAULT, lpszIniPathName );
  pProfile->yPos       = (INT) GetPrivateProfileInt(  lpszAppTitle, TEXT( "yPos" ),       CW_USEDEFAULT, lpszIniPathName );
  pProfile->nWidth     = (INT) GetPrivateProfileInt(  lpszAppTitle, TEXT( "nWidth" ),     CW_USEDEFAULT, lpszIniPathName );
  pProfile->nHeight    = (INT) GetPrivateProfileInt(  lpszAppTitle, TEXT( "nHeight" ),    CW_USEDEFAULT, lpszIniPathName );
  pProfile->fMaximized = (BOOL) GetPrivateProfileInt( lpszAppTitle, TEXT( "fMaximized" ), (INT) FALSE,   lpszIniPathName );
  pProfile->fMinimized = (BOOL) GetPrivateProfileInt( lpszAppTitle, TEXT( "fMinimized" ), (INT) FALSE,   lpszIniPathName );

  pProfile->fToolBar         = (BOOL) GetPrivateProfileInt( lpszAppTitle, TEXT( "fToolBar" ),         (INT) TRUE,  lpszIniPathName );
  pProfile->fSavedDirectory  = (BOOL) GetPrivateProfileInt( lpszAppTitle, TEXT( "fSavedDirectory" ),  (INT) TRUE,  lpszIniPathName );
  GetPrivateProfileString( lpszAppTitle, TEXT( "szInitialDir" ), NULL, pProfile->szInitialDir,
    sizeof( pProfile->szInitialDir ), lpszIniPathName );

  pProfile->LogFont.lfHeight         = (LONG) GetPrivateProfileInt( lpszAppTitle, TEXT( "lfHeight" ),         (INT) 13   ,       lpszIniPathName );
  pProfile->LogFont.lfWidth          = (LONG) GetPrivateProfileInt( lpszAppTitle, TEXT( "lfWidth" ),          (INT) 0    ,       lpszIniPathName );
  pProfile->LogFont.lfWeight         = (LONG) GetPrivateProfileInt( lpszAppTitle, TEXT( "lfWeight" ),         (INT) 400  ,       lpszIniPathName );
  pProfile->LogFont.lfItalic         = (BYTE) GetPrivateProfileInt( lpszAppTitle, TEXT( "lfItalic" ),         (INT) FALSE,       lpszIniPathName );
  pProfile->LogFont.lfUnderline      = (BYTE) GetPrivateProfileInt( lpszAppTitle, TEXT( "lfUnderline" ),      (INT) FALSE,       lpszIniPathName );
  pProfile->LogFont.lfStrikeOut      = (BYTE) GetPrivateProfileInt( lpszAppTitle, TEXT( "lfStrikeOut" ),      (INT) FALSE,       lpszIniPathName );
  pProfile->LogFont.lfPitchAndFamily = (BYTE) GetPrivateProfileInt( lpszAppTitle, TEXT( "lfPitchAndFamily" ), (INT) FF_DONTCARE, lpszIniPathName );
  GetPrivateProfileString( lpszAppTitle, TEXT( "lfFaceName" ), TEXT( "System" ), pProfile->LogFont.lfFaceName,
    LF_FACESIZE, lpszIniPathName );
  pProfile->rgbForeColor = (COLORREF) GetPrivateProfileInt( lpszAppTitle, TEXT( "rgbForeColor" ),
                            (INT) GetSysColor(COLOR_WINDOWTEXT), lpszIniPathName );
  pProfile->rgbBackColor = (COLORREF) GetPrivateProfileInt( lpszAppTitle, TEXT( "rgbBackColor" ),
                            (INT) GetSysColor(COLOR_WINDOW), lpszIniPathName );

  pProfile->fClearOnNew      = (BOOL) GetPrivateProfileInt( lpszAppTitle, TEXT( "fClearOnNew" ),      (INT) TRUE,  lpszIniPathName );
  pProfile->fVerbose         = (BOOL) GetPrivateProfileInt( lpszAppTitle, TEXT( "fVerbose" ),         (INT) FALSE, lpszIniPathName );
  pProfile->fShowSymbols     = (BOOL) GetPrivateProfileInt( lpszAppTitle, TEXT( "fShowSymbols" ),     (INT) FALSE, lpszIniPathName );
  pProfile->DebugMode        = (LONG) GetPrivateProfileInt( lpszAppTitle, TEXT( "DebugMode" ),        (INT) DEBUG_PROCESS        , lpszIniPathName );
  pProfile->DebuggeePriority = (LONG) GetPrivateProfileInt( lpszAppTitle, TEXT( "DebuggeePriority" ), (INT) NORMAL_PRIORITY_CLASS, lpszIniPathName );
  pProfile->DebugErrorLevel  = (DWORD) GetPrivateProfileInt( lpszAppTitle, TEXT( "DebugErrorLevel" ), (INT) 0, lpszIniPathName );
  pProfile->fSavePreferences = (BOOL) GetPrivateProfileInt( lpszAppTitle, TEXT( "fSavePreferences" ), (INT) FALSE, lpszIniPathName );

  pProfile->fSaveOnExit      = (BOOL) GetPrivateProfileInt( lpszAppTitle, TEXT( "fSaveOnExit" ),      (INT) TRUE,  lpszIniPathName );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : WritePrivateProfileSettings( LPCTSTR, LPCTSTR, PPROFILE )
// PURPOSE  : Saves the preferences and options to the profile (registry)
// COMMENTS :
// ************************************************************************
BOOL
WritePrivateProfileSettings( LPCTSTR lpszAppTitle, LPCTSTR lpszIniPathName,
  PPROFILE pProfile )
{
  if( pProfile->fSaveOnExit ) {
    WritePrivateProfileInt( lpszAppTitle, TEXT( "xPos" ),       (INT) pProfile->xPos,       lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "yPos" ),       (INT) pProfile->yPos,       lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "nWidth" ),     (INT) pProfile->nWidth,     lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "nHeight" ),    (INT) pProfile->nHeight,    lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "fMaximized" ), (INT) pProfile->fMaximized, lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "fMinimized" ), (INT) pProfile->fMinimized, lpszIniPathName );

    WritePrivateProfileInt( lpszAppTitle, TEXT( "fToolBar" ),         (INT) pProfile->fToolBar,         lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "fSavedDirectory" ),  (INT) pProfile->fSavedDirectory,  lpszIniPathName );
    WritePrivateProfileString( lpszAppTitle, TEXT( "szInitialDir" ), pProfile->szInitialDir, lpszIniPathName );

    WritePrivateProfileInt( lpszAppTitle, TEXT( "lfHeight" ),         (INT) pProfile->LogFont.lfHeight,         lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "lfWidth" ),          (INT) pProfile->LogFont.lfWidth,          lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "lfWeight" ),         (INT) pProfile->LogFont.lfWeight,         lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "lfItalic" ),         (INT) pProfile->LogFont.lfItalic,         lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "lfUnderline" ),      (INT) pProfile->LogFont.lfUnderline,      lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "lfStrikeOut" ),      (INT) pProfile->LogFont.lfStrikeOut,      lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "lfPitchAndFamily" ), (INT) pProfile->LogFont.lfPitchAndFamily, lpszIniPathName );
    WritePrivateProfileString( lpszAppTitle, TEXT( "lfFaceName" ), pProfile->LogFont.lfFaceName, lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "rgbForeColor" ), (INT) pProfile->rgbForeColor, lpszIniPathName );
    WritePrivateProfileInt( lpszAppTitle, TEXT( "rgbBackColor" ), (INT) pProfile->rgbBackColor, lpszIniPathName );

    if( pProfile->fSavePreferences ) {
      WritePrivateProfileInt( lpszAppTitle, TEXT( "fClearOnNew" ),  (INT) pProfile->fClearOnNew,  lpszIniPathName );
      WritePrivateProfileInt( lpszAppTitle, TEXT( "fVerbose" ),     (INT) pProfile->fVerbose,     lpszIniPathName );
      WritePrivateProfileInt( lpszAppTitle, TEXT( "fShowSymbols" ), (INT) pProfile->fShowSymbols, lpszIniPathName );
      WritePrivateProfileInt( lpszAppTitle, TEXT( "DebugMode" ),        (INT) pProfile->DebugMode,        lpszIniPathName );
      WritePrivateProfileInt( lpszAppTitle, TEXT( "DebuggeePriority" ), (INT) pProfile->DebuggeePriority, lpszIniPathName );
      WritePrivateProfileInt( lpszAppTitle, TEXT( "DebugErrorLevel" ), (INT) pProfile->DebugErrorLevel, lpszIniPathName );
    }
    WritePrivateProfileInt( lpszAppTitle, TEXT( "fSavePreferences" ), (INT) pProfile->fSavePreferences, lpszIniPathName );
  }
  WritePrivateProfileInt( lpszAppTitle, TEXT( "fSaveOnExit" ), (INT) pProfile->fSaveOnExit, lpszIniPathName );

  return( TRUE );
}

// ************************************************************************
// FUNCTION: WritePrivateProfileInt( LPCTSTR, LPCTSTR, INT, LPCTSTR )
// PURPOSE : Writes the value of an integer from a specified keyname within
//           a specified section of the Private Profile
// COMMENTS: Matched pair to GetPrivateProfileInt
// ************************************************************************
BOOL
WritePrivateProfileInt( LPCTSTR lpAppName, LPCTSTR lpKeyName, INT Value, LPCTSTR lpFileName )
{
  TCHAR ValBuf[16];

  wsprintf( ValBuf, TEXT( "%i" ), Value);

  return( WritePrivateProfileString( lpAppName, lpKeyName, ValBuf, lpFileName ) );
}

// ************************************************************************
// FUNCTION : UpdateMenuSettings( HWND )
// PURPOSE  : Adds check marks to active menu items
// COMMENTS :
// ************************************************************************
BOOL
UpdateMenuSettings( HWND hWnd )
{
  if( Profile.fToolBar )
    CheckMenuItem( GetMenu(hWnd), IDM_OPTIONS_TOOLBAR, MF_CHECKED );
  if( Profile.fSavedDirectory )
    CheckMenuItem( GetMenu(hWnd), IDM_OPTIONS_SAVEDDIR, MF_CHECKED );
  if( Profile.fSaveOnExit )
    CheckMenuItem( GetMenu(hWnd), IDM_OPTIONS_SAVEONEXIT, MF_CHECKED );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : OutOfMemoryMessageBox( HWND )
// PURPOSE  : If called, displays an "Out of Memory" message box
// COMMENTS :
// ************************************************************************
BOOL
OutOfMemoryMessageBox( HWND hWndOwner )

{
  MessageBox(
    hWndOwner,
    TEXT( "Out of Memory" ),
    TEXT( "Memory Error" ),
    MB_ICONSTOP | MB_APPLMODAL );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : MaxDebuggeesMessageBox( HWND )
// PURPOSE  : If called, displays an "Max Debuggees" message box
// COMMENTS :
// ************************************************************************
BOOL
MaxDebuggeesMessageBox( HWND hWndOwner )
{
  MessageBox( hWndOwner,
    TEXT( "This build of the Debug Event Browser\n" )
    TEXT( "is limited to debugging only one debuggee\n" )
    TEXT( "at a time." ),
    TEXT( "Cannot Open Debuggee" ),
    MB_OK | MB_ICONEXCLAMATION );

  return( TRUE );
}

// ========================================================================
// helper functions
// ========================================================================

// ************************************************************************
// FUNCTION : ErrorMessageBox( LPCTSTR, LPCTSTR, LPCTSTR, INT )
// PURPOSE  : Displays an error message box with various error information
//            and allows the user to terminate or continue the process.
//            For a Win32 Application, GetLastError and FormatMessage are
//            used to retrieve the last API error code and error message.
// COMMENTS :
// ************************************************************************
BOOL
ErrorMessageBox( LPCTSTR lpszText, LPCTSTR lpszTitle, LPCTSTR lpszFile,
  INT Line )
{
  #define ERROR_BUFFER_SIZE 512

  static TCHAR Format[] =
    TEXT( "%s\n\n"                                  )
    TEXT( "-- Error Information --\n"               )
    TEXT( "File : %s\n"                             )
    TEXT( "Line : %d\n"                             )
    TEXT( "Error Number : %d\n"                     )
    TEXT( "Error Message : %s\n"                    )
    TEXT( "\n"                                      )
    TEXT( "Press OK to terminate this application." );

  LPTSTR lpFormatMessageBuffer;
  DWORD  dwFormatMessage;
  DWORD  dwGetLastError;
  HLOCAL hMessageBoxBuffer;
  LPVOID lpMessageBoxBuffer;

  //-- perform a simple check on the needed buffer size
  if( lstrlen(lpszText) > (ERROR_BUFFER_SIZE - lstrlen(Format)) )
    return( FALSE );

  //-- allocate the message box buffer
  hMessageBoxBuffer  = LocalAlloc( LMEM_MOVEABLE, ERROR_BUFFER_SIZE );
  lpMessageBoxBuffer = LocalLock( hMessageBoxBuffer );

  //-- get the system error and system error message
  dwGetLastError = GetLastError();
  dwFormatMessage = FormatMessage(
                      FORMAT_MESSAGE_ALLOCATE_BUFFER
                      | FORMAT_MESSAGE_FROM_SYSTEM,
                      NULL, dwGetLastError, LANG_NEUTRAL,
                      (LPTSTR) &lpFormatMessageBuffer, 0, NULL );
  if( !dwFormatMessage )
    lpFormatMessageBuffer = TEXT("FormatMessage() failed!");

  //-- format the error messge box string
  wsprintf( lpMessageBoxBuffer, Format, lpszText, lpszFile, Line,
    dwGetLastError, lpFormatMessageBuffer );

  // -- display the error and allow the user to terminate or continue
  if( MessageBox( NULL, lpMessageBoxBuffer, lpszTitle,
        MB_APPLMODAL | MB_ICONSTOP | MB_OKCANCEL )
    == IDOK )
    ExitProcess( 0 );

  //-- free all buffers
  if( dwFormatMessage )
    LocalFree( (HLOCAL) lpFormatMessageBuffer );
  LocalFree( (HLOCAL) hMessageBoxBuffer );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : SubclassWindow( HWND, WNDPROC )
// PURPOSE  : Subclasses a window procedure
// COMMENTS : Returns the old window procedure
// ************************************************************************
WNDPROC
SubclassWindow( HWND hWnd, WNDPROC NewWndProc)
{
  WNDPROC OldWndProc;

  OldWndProc = (WNDPROC) GetWindowLong( hWnd, GWL_WNDPROC );
  SetWindowLong( hWnd, GWL_WNDPROC, (LONG) NewWndProc );

  return OldWndProc;
}

// ************************************************************************
// FUNCTION : SendWmSizeMessage( HWND )
// PURPOSE  : Sends a WM_SIZE message containing the current size
// COMMENTS : Forces a WM_SIZE message without actually changing the size
// ************************************************************************
BOOL
SendWmSizeMessage( HWND hWnd )
{
  RECT Rect;

  if( !GetClientRect( hWnd, &Rect ) )
    return( FALSE );

  return( SendMessage( hWnd, WM_SIZE, SIZENORMAL,
            MAKELONG( Rect.right - Rect.left, Rect.bottom - Rect.top) ) );
}

// ************************************************************************
// FUNCTION : GetPathFromFullPathName( LPCTSTR, LPTSTR, UINT )
// PURPOSE  : Extracts the path given a full pathname
// COMMENTS :
// ************************************************************************
UINT
GetPathFromFullPathName( LPCTSTR lpFullPathName, LPTSTR lpPathBuffer,
  UINT nPathBufferLength )
{
  UINT nLength;
  int i, j;

  if( (nLength = (UINT) lstrlen( lpFullPathName ) ) > nPathBufferLength )
    return( nLength );

  lstrcpy( lpPathBuffer, lpFullPathName );

  for( j = 0, i = nLength; (UINT)j < nLength;
                  j += IsDBCSLeadByte(lpPathBuffer[j]) ? 2 : 1){
      if( lpPathBuffer[j] == '\\' || lpPathBuffer[j] == ':' ){
          i = j;
      }
  }
  if( lpPathBuffer[i] == ':' )
    lpPathBuffer[i+1] = '\0';
  else
    lpPathBuffer[i] = '\0';

  return( (UINT) i );
}

// ************************************************************************
// FUNCTION : CopyListBoxToClipboard( HWND, LONG )
// PURPOSE  : Copies the entire contents of a listbox into the clipboard.
// COMMENTS : Returns TRUE on success, FALSE otherwise
// ************************************************************************
BOOL
CopyListBoxToClipboard( HWND hWndListBox, LONG MaxStrLen )
{
  LPTSTR  lpDataBuffer;
  HGLOBAL hDataBuffer;

  TCHAR  TempBuffer[256];
  DWORD  dwItemCount;
  DWORD  Count;
  LONG   StrLen;
  DWORD  dwMemSize;

  dwItemCount = (DWORD) SendMessage( hWndListBox, LB_GETCOUNT, 0 , 0 );
  dwMemSize = dwItemCount * (DWORD) MaxStrLen;

  //-- limit the size copied to the clipboard
  if( dwMemSize > 0xFFFFF )
    dwMemSize = 0xFFFFF;

  if( !(hDataBuffer = GlobalAlloc( GMEM_DDESHARE, dwMemSize ) ) ) {
    OutOfMemoryMessageBox( GetFocus() );
    return( FALSE );
  }
  if( !(lpDataBuffer = (LPTSTR) GlobalLock( hDataBuffer ) ) ) {
    GlobalFree( hDataBuffer );
    OutOfMemoryMessageBox( GetFocus() );
    return( FALSE );
  }

  *lpDataBuffer = '\0';

  for( Count = 0; Count < dwItemCount; Count++ ) {
    StrLen = SendMessage( hWndListBox, LB_GETTEXTLEN, Count, 0L );
    if( StrLen > (sizeof(TempBuffer)-3) )
      continue;
    StrLen = SendMessage( hWndListBox, LB_GETTEXT, Count, (LPARAM) TempBuffer );
    TempBuffer[StrLen]   = '\r';
    TempBuffer[StrLen+1] = '\n';
    TempBuffer[StrLen+2] = '\0';
    lstrcat( lpDataBuffer, TempBuffer );
  }

  GlobalUnlock( hDataBuffer );

  if( !OpenClipboard( hWndListBox ) ) {
    Sleep( 250 );  // wait a quarter second and try again.
    if( !OpenClipboard( hWndListBox ) ) {
      MessageBox( GetFocus(),
        TEXT( "Could not open the Clipboard!" ),
        TEXT( "Cannot Open Clipboard" ),
        MB_ICONSTOP | MB_APPLMODAL );
      GlobalFree( hDataBuffer );
      return( FALSE );
    }
  }
  if( !EmptyClipboard() ) {
    MessageBox( GetFocus(),
      TEXT( "Could not empty the Clipboard!" ),
      TEXT( "Cannot Empty Clipboard" ),
      MB_ICONSTOP | MB_APPLMODAL );
    GlobalFree( hDataBuffer );
    return( FALSE );
  }
  if( !SetClipboardData( CF_TEXT, hDataBuffer ) ) {
    MessageBox( GetFocus(),
      TEXT( "Could not copy data to the Clipboard!" ),
      TEXT( "Cannot Set Clipboard Data" ),
      MB_ICONSTOP | MB_APPLMODAL );
    GlobalFree( hDataBuffer );
    return( FALSE );
  }
  CloseClipboard();

  return( TRUE );
}

// ************************************************************************
// FUNCTION : ListBoxInsert( HWND, LPLONG, LPCTSTR )
// PURPOSE  : Inserts the string into the listbox.
// COMMENTS : Returns the index of the string inserted
// ************************************************************************
LONG
ListBoxInsert( HWND hWndListBox, LPLONG lpMaxStrLen, LPCTSTR lpszString )
{
  static LONG MaxTextExtent = 0;

  LONG        Index;

  if( lpszString == NULL ) {
    MaxTextExtent = 0;
    SendMessage( hWndListBox, LB_SETHORIZONTALEXTENT, 0, 0 );
    return( 0 );
  }

  if( hWndListBox != NULL ) {
    HDC  hDC;
    SIZE Size;
    LONG StrLen;

    if( (StrLen = lstrlen( lpszString)) > *lpMaxStrLen )
      *lpMaxStrLen = StrLen;
    hDC = GetDC( hWndListBox );
    GetTextExtentPoint( hDC, lpszString, StrLen, &Size );
    ReleaseDC( hWndListBox, hDC );
    if( Size.cx > MaxTextExtent ) {
      MaxTextExtent = Size.cx;
      SendMessage( hWndListBox, LB_SETHORIZONTALEXTENT, (WPARAM) (MaxTextExtent*1.1), 0 );
    }
    Index = SendMessage( hWndListBox, LB_ADDSTRING, 0, (LPARAM) lpszString );
    SendMessage( hWndListBox, LB_SETCURSEL, Index, 0 );
  }

  return( Index );
}

// ************************************************************************
// FUNCTION : ListBoxPrintF( HWND, LPCTSTR, ... )
// PURPOSE  : Inserts the string format into the listbox.
// COMMENTS : Returns the index of the last string inserted
// ************************************************************************
LONG
ListBoxPrintF( HWND hWndListBox, LPCTSTR szFormat, ... )
{
  static TCHAR szBuffer[ 1024 ];

  va_list valist;
  LONG    Index;
  LPTSTR  lpCurrentString, lpCurrentChar;

  va_start( valist, szFormat );
  wvsprintf( szBuffer, szFormat, valist );
  va_end( valist );

  //-- insert strings line-by-line
  for( lpCurrentString = lpCurrentChar = szBuffer; *lpCurrentChar != '\0'; ) {
    if( *lpCurrentChar == '\n' ) {
      LPTSTR lpNextChar = CharNext( lpCurrentChar );
      *lpCurrentChar = '\0';
      Index = ListBoxInsert( hWndListBox, &(Global.MaxStrLen), lpCurrentString );
      lpCurrentString = lpCurrentChar = lpNextChar;
    }
    else
      lpCurrentChar = CharNext( lpCurrentChar );
  }
  Index = ListBoxInsert( hWndListBox, &(Global.MaxStrLen), lpCurrentString );

  return( Index );
}

// ************************************************************************
// FUNCTION : StringPrintF( LPTSTR, LPCTSTR, ... )
// PURPOSE  : Formats a string buffer according to the format-control
//            string.  This function is wsprintf but the return
//            values is a pointer to the string instead of the count.
// COMMENTS :
// ************************************************************************
LPTSTR
StringPrintF( LPTSTR lpString, LPCTSTR szFormat, ...  )
{
  va_list valist;

  va_start( valist, szFormat );
  wvsprintf( lpString, szFormat, valist );
  va_end( valist );

  return( lpString );
}

// ************************************************************************
// FUNCTION : StringAppendF( LPTSTR, LPCTSTR, ... )
// PURPOSE  : Append a variable number of characters and values to the end
//            of an existing string buffer according to the format string
//            (which uses standard printf() formating notation).
// COMMENTS :
// ************************************************************************
BOOL
StringAppendF( LPTSTR lpszBuffer, LPCTSTR szFormat, ... )
{
  static TCHAR szLgBuffer[1024];

  va_list valist;
  int n;

  //-- add event specific information
  va_start( valist, szFormat );
  n = wvsprintf( szLgBuffer, szFormat, valist );
  va_end( valist );

  //-- append information to the string buffer
  lstrcat( lpszBuffer, szLgBuffer );

  return( TRUE );
}


DEBMISC.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#ifndef DEBMISC_H

 #define DEBMISC_H

 #ifndef DEBDEBUG_H
   #include "DEBDebug.H"
 #endif

 #ifndef DEBMAIN_H
   #include "DEBMain.H"
 #endif

 //-- function prototypes
 BOOL          StartDebuggee( LPTSTR, HWND );
 BOOL          AttachToDebuggee( DWORD, HWND );
 BOOL CALLBACK EnumProcessListFunc( HWND, LPARAM );
 BOOL          GetDebuggeeFileName( LPTSTR, HWND );
 BOOL          ChooseNewFont( HWND );
 BOOL          ChooseNewBackColor( HWND );
 BOOL          MakeCommonDebugEventString( LPTSTR, LPDEBUG_EVENT);
 HWND          CreateTextButtonBar( HWND, LPINT );
 HWND          CreateIconWindow( HWND, LPCTSTR );
 BOOL          GetPrivateProfileSettings( LPCTSTR, LPCTSTR, PPROFILE );
 BOOL          WritePrivateProfileSettings( LPCTSTR, LPCTSTR, PPROFILE );
 BOOL          WritePrivateProfileInt( LPCTSTR, LPCTSTR, INT, LPCTSTR );
 BOOL          UpdateMenuSettings( HWND );
 BOOL          OutOfMemoryMessageBox( HWND );
 BOOL          MaxDebuggeesMessageBox( HWND );
 BOOL          ErrorMessageBox( LPCTSTR, LPCTSTR, LPCTSTR, INT );
 WNDPROC       SubclassWindow( HWND, WNDPROC );
 BOOL          SendWmSizeMessage( HWND );
 UINT          GetPathFromFullPathName( LPCTSTR, LPTSTR, UINT );
 BOOL          CopyListBoxToClipboard( HWND, LONG );
 LONG          ListBoxInsert( HWND, LPLONG, LPCTSTR );
 LONG          ListBoxPrintF( HWND, LPCTSTR, ... );
 LPTSTR        StringPrintF( LPTSTR, LPCTSTR, ...  );
 BOOL          StringAppendF( LPTSTR, LPCTSTR, ... );

#endif // DEBMISC_H


LINKLIST.C

// ************************************************************************
//
//                      Microsoft Developer Support
//             Copyright 1992 - 1998 Microsoft Corporation
//
// ************************************************************************
// MODULE    : LinkList.C - Ordered Double Linked List Package
// PURPOSE   : Provide a general, sorted, double linked list package
// FUNCTIONS :
//   CreateList        - allocate memory for a new list, registers the
//                       ordering function
//   CreateNode        - allocate memory fpr a new node that can be put
//                       into the linked list
//   InsertNode        - insert a new node into the list
//   SetCurrentNode    - finds the 1st occurence of a node that matches
//                       a key(s) according to the match function
//                       and sets it as the current node
//   SetCurrentNodeEx  - finds the Nth occurence of a node that matches
//                       a key(s) according to the match function
//                       and sets it as the current node
//   GetCurrentNode    - get the current node
//   GetFirstNode      - get the first (left-most) node in the list
//   GetLastNode       - get the last (right-most) node in the list
//   GetNextNode       - get the next (right) node from the current
//   GetPrevNode       - get the previous (left) node from the current
//   DeleteCurrentNode - deletes the current node from the list and
//                       frees the memory associated with it
//   DestroyNode       - deallocates the memory associated with a node
//   DestroyList       - deallocates the memory associated with a list,
//                       does not delete any of the nodes
//   GetListError      - gets the current list error
//
// COMMENTS  : The application must serialize access to the linked list
//
//   Format of the Ordering and Matching functions is as follows:
//   -----------------------------------------------------------
//
//   int OrderFunc( PNODE pNodeOne, PNODE pNodeTwo )
//   {
//     if( (pNodeOne->pNodeData).SortKey <  (pNodeTwo->pNodeData).SortKey )
//       return( LIST_LEFT_OF  );
//     if( (pNodeOne->pNodeData).SortKey == (pNodeTwo->pNodeData).SortKey )
//       return( LIST_MATCH    );
//     if( (pNodeOne->pNodeData).SortKey  > (pNodeTwo->pNodeData).SortKey )
//       return( LIST_RIGHT_OF );
//   }
//
// ************************************************************************
#include <StdLib.H>     // malloc(), free()
#include "LinkList.H"

//-- NULL pointer dereferencing checking macro
#define CHECK_POINTER( ppNode )                                       \
          if( ppNode == NULL ) {                                      \
            return( pList->ListError = LIST_ERROR_DEREFERENCE_NULL ); \
          }

// ************************************************************************
// FUNCTION : CreateList( PLIST* ppList, int (*)(PNODE, PNODE) ) )
// PURPOSE  : allocate a new list, registers the ordering function
// COMMENTS : sets all pointers to NULL
// ************************************************************************
BOOL
CreateList( PLIST* ppList, int (*OrderFunction)( PNODE pNodeOne, PNODE pNodeTwo ) )
{
  *ppList = (PLIST) malloc( sizeof(LIST) );
  if( ppList == NULL ) {
    (*ppList)->ListError = LIST_NO_ERROR;
    return( FALSE );
  }

  //-- register the link list data ordering function
  (*ppList)->OrderFunction = OrderFunction;

  //-- initialize the general linked list node pointers to NULL
  (*ppList)->pFirstNode   = NULL;
  (*ppList)->pCurrentNode = NULL;
  (*ppList)->pLastNode    = NULL;

  //-- indicate no error
  (*ppList)->ListError  = LIST_NO_ERROR;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : CreateNode( PNODE* )
// PURPOSE  : create/allocate a new node that can be put into list
// COMMENTS :
// ************************************************************************
BOOL
CreateNode( PNODE* ppNewNode )
{
  *ppNewNode = (PNODE) malloc( sizeof(NODE) );
  if( *ppNewNode == NULL ) {
    return( FALSE );
  }

  //-- initialize the node pointers to NULL
  (*ppNewNode)->pRightLink = NULL;
  (*ppNewNode)->pLeftLink  = NULL;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : InsertNode( PLIST pList, PNODE )
// PURPOSE  : insert a new node into the list
// COMMENTS :
//   All nodes must have a unique key entry (from order function), if a
//   match is found during an insert then the old node gets replaced with
//   the new node.
//
//  NOTE: changes pList->pCurrentNode
// ************************************************************************
BOOL
InsertNode( PLIST pList, PNODE pNewNode )
{
  int Position;
  int LastPosition;

  //-- if this is the first node in a new list
  if( pList->pFirstNode == NULL ) {
    pList->pFirstNode   = pNewNode;
    pList->pLastNode    = pNewNode;
    pList->pCurrentNode = pNewNode;
    pList->ListError    = LIST_NO_ERROR;
    return( TRUE );
  }

  Position = (*(pList->OrderFunction))( pNewNode, pList->pCurrentNode );

  //-- search for insertion point
  while( pList->pCurrentNode != NULL ) {

    LastPosition = Position;
    Position = (*(pList->OrderFunction))(pNewNode, pList->pCurrentNode);

    if( pList->pFirstNode == pList->pLastNode )
      break;

    if( Position != LastPosition )
      break;

    if( pList->pCurrentNode == pList->pFirstNode ) {
      if( Position == LIST_RIGHT_OF ) {
        pList->pCurrentNode = pList->pCurrentNode->pRightLink;
        continue;
      }
      break;
    }

    if( pList->pCurrentNode == pList->pLastNode ) {
      if( Position == LIST_LEFT_OF ) {
        pList->pCurrentNode = pList->pCurrentNode->pLeftLink;
        continue;
      }
      break;
    }

    if( Position == LastPosition ) {
      if( Position == LIST_LEFT_OF) {
        pList->pCurrentNode = pList->pCurrentNode->pLeftLink;
        continue;
      }
      if( Position == LIST_RIGHT_OF ) {
        pList->pCurrentNode = pList->pCurrentNode->pRightLink;
        continue;
      }
      break;
    }

  }

  //-- now, insert the pNewNode
  switch( Position ) {

    case LIST_LEFT_OF:
      pNewNode->pRightLink = pList->pCurrentNode;
      pNewNode->pLeftLink  = pList->pCurrentNode->pLeftLink;
      if( pList->pCurrentNode == pList->pFirstNode )
        pList->pFirstNode = pNewNode;
      else
        pList->pCurrentNode->pLeftLink->pRightLink = pNewNode;
      pList->pCurrentNode->pLeftLink = pNewNode;
      break;

  #if 1  // replace duplicates with new data

    case LIST_MATCH:
      pNewNode->pLeftLink  = pList->pCurrentNode->pLeftLink;
      pNewNode->pRightLink = pList->pCurrentNode->pRightLink;
      if( pList->pCurrentNode == pList->pLastNode )
        pList->pLastNode = pNewNode;
      else
        pList->pCurrentNode->pRightLink->pLeftLink = pNewNode;
      if( pList->pCurrentNode == pList->pFirstNode )
        pList->pFirstNode = pNewNode;
      else
        pList->pCurrentNode->pLeftLink->pRightLink = pNewNode;
      // note: doesn't destroy extra data associated with the node
      DestroyNode( pList->pCurrentNode );
      break;

  #else // or allow duplicates

    case LIST_MATCH:

  #endif

    case LIST_RIGHT_OF:
      pNewNode->pLeftLink  = pList->pCurrentNode;
      pNewNode->pRightLink = pList->pCurrentNode->pRightLink;
      if( pList->pCurrentNode == pList->pLastNode )
        pList->pLastNode = pNewNode;
      else
        pList->pCurrentNode->pRightLink->pLeftLink = pNewNode;
      pList->pCurrentNode->pRightLink = pNewNode;
      break;

  }
  pList->pCurrentNode = pNewNode;
  pList->ListError    = LIST_NO_ERROR;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : SetCurrentNode( PLIST pList, PNODE, int (*)(PNODE, PNODE) )
// PURPOSE  : finds the first occurence of a node from the list that matches
//            a key(s) according to the match function.
// COMMENTS :
//   May use the ordering function or a new matching function if not
//   searching for a match based on the primary ordering key.
//   NOTE: the matching and ordering functions have the same definition.
//   NOTE: changes pList->pCurrentNode.
// ************************************************************************
BOOL
SetCurrentNode( PLIST pList, PNODE pKeyNode,
         int (*MatchFunction)( PNODE pNodeOne, PNODE pNodeTwo ) )
{
  return( SetCurrentNodeEx( pList, pKeyNode, MatchFunction, 1 ) );
}

// ************************************************************************
// FUNCTION : SetCurrentNodeEx( PLIST pList, PNODE, int (*)(PNODE, PNODE), int )
// PURPOSE  : finds the Nth occurence of a node from the list that matches
//            a key(s) according to the match function.
// COMMENTS :
//   May use the ordering function or a new matching function if not
//   searching for a match based on the primary ordering key.
//   NOTE: the matching and ordering functions have the same definition.
//   NOTE: changes pList->pCurrentNode.
// ************************************************************************
BOOL
SetCurrentNodeEx( PLIST pList, PNODE pKeyNode,
  int (*MatchFunction)( PNODE pNodeOne, PNODE pNodeTwo ),
  int Occurence )
{
  int Position;

  //-- if list is empty, exit
  if( pList->pCurrentNode == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }

  //-- if match and order are same function
  if( MatchFunction == (pList->OrderFunction) ) {
    int  LastPosition;

    LastPosition = Position = (*MatchFunction)(pKeyNode, pList->pCurrentNode);

    while( Occurence ) {
      if( ( Position == LIST_LEFT_OF ) && ( LastPosition == LIST_LEFT_OF )
          && ( pList->pCurrentNode != pList->pFirstNode ) )
        pList->pCurrentNode = pList->pCurrentNode->pLeftLink;
      else if( ( Position == LIST_RIGHT_OF ) && ( LastPosition == LIST_RIGHT_OF )
            && ( pList->pCurrentNode != pList->pLastNode ) )
        pList->pCurrentNode = pList->pCurrentNode->pRightLink;
      else {
        Occurence--;
        continue;
      }
      LastPosition = Position;
      Position = (*MatchFunction)(pKeyNode, pList->pCurrentNode);
    }

  }
  //-- match and order are not the same function, thus start
  //   the search at the front of the list
  else {
    pList->pCurrentNode = pList->pFirstNode;
    while( (Occurence > 0) && ( (pList->pCurrentNode) != NULL ) ) {
      Position = (*MatchFunction)(pKeyNode, pList->pCurrentNode);
      if( Position == LIST_MATCH )
        Occurence--;
      if( Occurence > 0 )
        pList->pCurrentNode = pList->pCurrentNode->pRightLink;
    }
  }

  if( ( Position == LIST_MATCH ) && ( Occurence == 0 ) ) {
    pList->ListError = LIST_NO_ERROR;
    return( TRUE );
  }
  pList->ListError = LIST_ERROR_NO_MATCH;

  return( FALSE );
}

// ************************************************************************
// FUNCTION : GetCurrentNode( PLIST pList, PNODE* )
// PURPOSE  : gets the current node from the list
// COMMENTS :
//   Does not change pList->pCurrentNode.
//   Changes ppNode, thus do not pass in a ppNode which has additional
//   memory associated with it.
// ************************************************************************
BOOL
GetCurrentNode( PLIST pList, PNODE* ppNode )
{
  CHECK_POINTER( ppNode );
  if( pList->pCurrentNode == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }
  *ppNode = pList->pCurrentNode;
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : GetFirstNode( PLIST pList, PNODE* )
// PURPOSE  : gets the first (left-most) node from the list
// COMMENTS :
//   Does not change pList->pCurrentNode.
//   Changes ppNode, thus do not pass in a ppNode which has additional
//   memory associated with it.
// ************************************************************************
BOOL
GetFirstNode( PLIST pList, PNODE* ppNode )
{
  CHECK_POINTER( ppNode );
  if( pList->pFirstNode == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }
  *ppNode = pList->pFirstNode;
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : GetLastNode( PLIST pList, PNODE* )
// PURPOSE  : get the last (right-most) node from the List
// COMMENTS :
//   Does not change pList->pCurrentNode.
//   Changes ppNode, thus do not pass in a ppNode which has additional
//   memory associated with it.
// ************************************************************************
BOOL
GetLastNode( PLIST pList, PNODE* ppNode )
{
  CHECK_POINTER( ppNode );
  if( pList->pLastNode == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }
  *ppNode = pList->pLastNode;
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : GetNextNode( PLIST pList, PNODE* )
// PURPOSE  : get the next (right) node from the pList->pCurrentNode
// COMMENTS :
//   Does not change pList->pCurrentNode.
//   Changes ppNode, thus do not pass in a ppNode which has additional
//   memory associated with it.
// ************************************************************************
BOOL
GetNextNode( PLIST pList, PNODE* ppNode )
{
  CHECK_POINTER( ppNode );
  if( (*ppNode)->pRightLink == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }
  *ppNode = (*ppNode)->pRightLink;
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : GetPrevNode( PLIST pList, PNODE* )
// PURPOSE  : get the previous (left) node from the pList->pCurrentNode
// COMMENTS :
//   Does not change pList->pCurrentNode.
//   Changes ppNode, thus do not pass in a ppNode which has additional
//   memory associated with it.
// ************************************************************************
BOOL
GetPrevNode( PLIST pList, PNODE* ppNode )
{
  CHECK_POINTER( ppNode );
  if( (*ppNode)->pLeftLink == NULL ) {
    pList->ListError = LIST_ERROR_NO_NODE;
    return( FALSE );
  }
  *ppNode = (*ppNode)->pLeftLink;
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DeleteCurrentNode( PLIST pList )
// PURPOSE  : deletes the current node from the list and frees the memory
//            associated with it
// COMMENTS :
//   Changes pList->pCurrentNode.
//
//   Typically, SetCurrentNode (or SetCurrentNodeEx) is called first to set
//   pList->pCurrentNode.  Also, any addtional memory associated with this
//   node should be freed first before calling this function.
// ************************************************************************
BOOL
DeleteCurrentNode( PLIST pList )
{
  PNODE pOldCurrentNode;

  if( pList->pCurrentNode != NULL ) {
    pOldCurrentNode = pList->pCurrentNode;

    if( pOldCurrentNode == pList->pFirstNode ) {
      pList->pFirstNode   = pOldCurrentNode->pRightLink;
      pList->pCurrentNode = pOldCurrentNode->pRightLink;
    }
    else {
      pOldCurrentNode->pLeftLink->pRightLink = pOldCurrentNode->pRightLink;
      pList->pCurrentNode = pOldCurrentNode->pLeftLink;
    }

    if( pOldCurrentNode == pList->pLastNode )
      pList->pLastNode = pOldCurrentNode->pLeftLink;
    else
      pOldCurrentNode->pRightLink->pLeftLink = pOldCurrentNode->pLeftLink;

    DestroyNode( pOldCurrentNode );
    pList->ListError = LIST_NO_ERROR;
    return( TRUE );
  }
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DestroyNode( PNODE pNode )
// PURPOSE  : deallocates a node
// COMMENTS :
// ************************************************************************
BOOL
DestroyNode( PNODE pNode )
{
  free( pNode );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : DestroyList( PLIST pList )
// PURPOSE  : deallocates a list, does not free any nodes, if present
// COMMENTS :
// ************************************************************************
BOOL
DestroyList( PLIST pList )
{
  free( pList );
  pList->ListError = LIST_NO_ERROR;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : GetListError( PLIST pList )
// PURPOSE  : get the last linked list error
// COMMENTS :
// ************************************************************************
int
GetListError( PLIST pList )
{
  return( pList->ListError );
}


LINKLIST.H

// ************************************************************************
//
//                      Microsoft Developer Support
//             Copyright 1992 - 1998 Microsoft Corporation
//
// ************************************************************************
// HEADER    : LinkList.H - Ordered Double Linked List Package
// PURPOSE   : Provide a general, sorted, double linked list package
// FUNCTIONS :
//   CreateList        - allocate memory for a new list, registers the
//                       ordering function
//   CreateNode        - allocate memory fpr a new node that can be put
//                       into the linked list
//   InsertNode        - insert a new node into the list
//   SetCurrentNode    - finds the 1st occurence of a node that matches
//                       a key(s) according to the match function
//                       and sets it as the current node
//   SetCurrentNodeEx  - finds the Nth occurence of a node that matches
//                       a key(s) according to the match function
//                       and sets it as the current node
//   GetCurrentNode    - get the current node
//   GetFirstNode      - get the first (left-most) node in the list
//   GetLastNode       - get the last (right-most) node in the list
//   GetNextNode       - get the next (right) node from the current
//   GetPrevNode       - get the previous (left) node from the current
//   DeleteCurrentNode - deletes the current node from the list and
//                       frees the memory associated with it
//   DestroyNode       - deallocates the memory associated with a node
//   DestroyList       - deallocates the memory associated with a list,
//                       does not delete any of the nodes
//   GetListError      - gets the current list error
//
// COMMENTS  : The application must serialize access to the linked list
//
//   Format of the Ordering and Matching functions is as follows:
//   -----------------------------------------------------------
//
//   int OrderFunc( PNODE pNodeOne, PNODE pNodeTwo )
//   {
//     if( (pNodeOne->pNodeData).SortKey < (pNodeTwo->pNodeData).SortKey )
//       return( LIST_LEFT_OF );
//     if( (pNodeOne->pNodeData).SortKey > (pNodeTwo->pNodeData).SortKey )
//       return( LIST_RIGHT_OF );
//     return( LIST_MATCH );
//   }
//
// ************************************************************************
#ifndef LINKLIST_H

 #define LINKLIST_H

 #ifdef __cplusplus
 extern "C" { /* Assume C declarations for C++ */
 #endif /* __cplusplus */

 // public typedefs and defines
 // -----------------------------------------------------------------------
 #define LIST_NO_ERROR                 1
 #define LIST_ERROR_NO_NODE           -1
 #define LIST_ERROR_NO_MATCH          -2
 #define LIST_ERROR_NO_FREE_MEM       -3
 #define LIST_ERROR_DEREFERENCE_NULL -99

 #define LIST_LEFT_OF   -1
 #define LIST_MATCH      0
 #define LIST_RIGHT_OF   1

 #ifndef TRUE
   #define TRUE  1
   #define FALSE 0
   typedef int   BOOL;
 #endif

 typedef void* PVOID;

 //-- definition of a node
 typedef struct NODE_STRUCT* PNODE;
 typedef struct NODE_STRUCT {
           PVOID pNodeData;
           PNODE pLeftLink;
           PNODE pRightLink;
         } NODE;

 //-- definition of a list
 typedef struct LIST_STRUCT* PLIST;
 typedef struct LIST_STRUCT {
           PVOID pListData;
           PNODE pFirstNode;
           PNODE pCurrentNode;
           PNODE pLastNode;
           int   (*OrderFunction)(PNODE, PNODE);
           int   ListError;
         } LIST;

 // public function prototypes
 // -----------------------------------------------------------------------
 BOOL CreateList( PLIST* ppList, int (*OrderFunction)( PNODE pNodeOne, PNODE pNodeTwo ) );
 BOOL CreateNode( PNODE* ppNewNode );
 BOOL InsertNode( PLIST pList, PNODE pNewNode );
 BOOL SetCurrentNode( PLIST pList, PNODE pKeyNode, int (*MatchFunction)( PNODE pNodeOne, PNODE pNodeTwo ) );
 BOOL SetCurrentNodeEx( PLIST pList, PNODE pKeyNode, int (*MatchFunction)( PNODE pNodeOne, PNODE pNodeTwo ), int Occurence );
 BOOL GetCurrentNode( PLIST pList, PNODE* ppNode );
 BOOL GetFirstNode( PLIST pList, PNODE* ppNode );
 BOOL GetLastNode( PLIST pList, PNODE* ppNode );
 BOOL GetNextNode( PLIST pList, PNODE* ppNode );
 BOOL GetPrevNode( PLIST pList, PNODE* ppNode );
 BOOL DeleteCurrentNode( PLIST pList );
 BOOL DestroyNode( PNODE pNode );
 BOOL DestroyList( PLIST pList );
 int  GetListError( PLIST pList );

 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

#endif // LINKLIST_H


TOOLBAR.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// ************************************************************************
// MODULE    : ToolBar.C
// PURPOSE   : A Win32 DLL containing a simple text ToolBar
// FUNCTIONS :
//   DllMain()         - Dll entry point (via _DllMainCRTStartup)
//   TextButtonBar()   - generate a text button ToolBar
//   TextButtonBarProc - processes messages for the TextButtonBarWClass
// COMMENTS  :
//
// ************************************************************************
#define   STRICT               // enable strict typing
#include <Windows.H>           // required for all Windows applications

#include "ToolBar.H"           // specific to this program

//-- internal data
HWND hWndToolBarOwner;         // window handle of Toolbar owner
                               // where all WM_COMMAND messages are sent

//-- internal prototypes
LRESULT CALLBACK TextButtonBarProc( HWND, UINT, WPARAM, LPARAM );

// ************************************************************************
// FUNCTION : DllMain( HINSTANCE, DWORD, LPVOID )
// PURPOSE  : DllMain is called by the C run-time library from the
//            _DllMainCRTStartup entry point.  The DLL entry point gets
//            called (entered) on the following events: "Process Attach",
//            "Thread Attach", "Thread Detach" or "Process Detach".
// COMMENTS : No initialization is needed here so this entry point simply
//            returns TRUE.
// ************************************************************************
BOOL WINAPI
DllMain( HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
  UNREFERENCED_PARAMETER( hInstDLL );
  UNREFERENCED_PARAMETER( fdwReason );
  UNREFERENCED_PARAMETER( lpvReserved );

  return( TRUE );
}

// ************************************************************************
// FUNCTION : TextButtonBar( HWND, LPTEXTBUTTON, LPINT )
// PURPOSE  : Generate a simple text button ToolBar
// COMMENTS : lpTextButton points to an array of TEXTBUTTON
//            structures.  The last structure must set the
//            member lpButtonText to NULL.  lpHeight contains the
//            height of the ToolBar window.
// ************************************************************************
HWND
TextButtonBar( HWND hWndParent, LPTEXTBUTTON lpTextButton, LPINT lpHeight )
{
  #define BUTTON_BORDER    4
  #define BUTTONBAR_BORDER 4

  static HWND         hWndTextButtonBar;

  HDC          hDC;
  WNDCLASS     WndClass;
  LONG         ButtonWidth;
  LONG         ButtonHeight;
  INT          xPosButton;
  INT          ButtonSpacing;
  INT          ButtonSpace;
  LPTSTR       lpButtonString;
  LPTEXTBUTTON lpTempTextButton;
  TEXTMETRIC   tm;
  SIZE         ButtonTextSize;
  RECT         rect;

  //-- store owner of Toolbar
  hWndToolBarOwner = hWndParent;

  //-- register the TestButtonBar window class
  WndClass.style         = (UINT) NULL;
  WndClass.lpfnWndProc   = (WNDPROC) TextButtonBarProc;
  WndClass.cbClsExtra    = 0;
  WndClass.cbWndExtra    = 0;
  WndClass.hInstance     = NULL;
  WndClass.hIcon         = NULL;
  WndClass.hCursor       = NULL;
  WndClass.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH);
  WndClass.lpszMenuName  = NULL;
  WndClass.lpszClassName = TEXT( "TextButtonBarWClass" );

  if( !RegisterClass( &WndClass ) )
    return( FALSE );

  hDC = GetDC( hWndParent );
  GetTextMetrics( hDC, &tm );
  ButtonHeight = 2 * BUTTON_BORDER + tm.tmHeight;
  *lpHeight = 2 * BUTTONBAR_BORDER + ButtonHeight;

  GetWindowRect( GetDesktopWindow(), &rect );
  hWndTextButtonBar = CreateWindow(
                        TEXT( "TextButtonBarWClass" ), TEXT( "Toolbar" ),
                        WS_CHILD | WS_CLIPSIBLINGS,
                        0, 0, rect.right - rect.left, *lpHeight,
                        hWndToolBarOwner,
                        (HMENU) NULL, (HANDLE) NULL, NULL );

  SetWindowPos( hWndTextButtonBar, (HWND) 1, 0, 0, 0, 0,
    SWP_NOMOVE | SWP_NOSIZE );

  //-- button spacing information
  ButtonSpacing = BUTTON_BORDER / 2;
  ButtonSpace   = BUTTON_BORDER * 4;
  xPosButton    = BUTTON_BORDER * 2;

  //-- Create all the button windows
  for( lpTempTextButton = lpTextButton;
         !( (lpTempTextButton->lpButtonText) == '\0'
           && (lpTempTextButton->idButton) == (UINT) NULL);
         lpTempTextButton++ ) {

    //-- if TB_SPACE then adjust new xPos and continue
    if( lpTempTextButton->idButton == TB_SPACE ) {
      xPosButton += ButtonSpace;
      continue;
    }

    lpButtonString = lpTempTextButton->lpButtonText;
    GetTextExtentPoint( hDC, lpButtonString, lstrlen(lpButtonString), &ButtonTextSize );
    ButtonWidth = (4 * BUTTON_BORDER) + (LONG) ButtonTextSize.cx;

    (lpTempTextButton->hWndButton) = CreateWindow(
         TEXT( "BUTTON" ), lpButtonString,
         WS_VISIBLE | BS_PUSHBUTTON | WS_CHILD,
         xPosButton, BUTTONBAR_BORDER, ButtonWidth, ButtonHeight,
         hWndTextButtonBar,
         (HMENU) lpTempTextButton->idButton,
         (HANDLE) NULL, NULL );

    xPosButton += ButtonWidth + ButtonSpacing;
  }
  ReleaseDC( hWndParent, hDC );

  return( hWndTextButtonBar );
}

// ************************************************************************
// FUNCTION : TextButtonBarProc( HWND, UINT, WPARAM, LPARAM )
// PURPOSE  : Processes messages for the TextButtonBarWClass
// MESSAGES :
//  WM_COMMAND - forwarded to the owner window
//  WM_PAINT   - draws a 3D background
// COMMENTS :
// ************************************************************************
LRESULT CALLBACK
TextButtonBarProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
  static HDC         hDC;
  static RECT        rect;
  static PAINTSTRUCT ps;

  switch( uMsg ) {

    case WM_COMMAND:
      SendMessage( hWndToolBarOwner, uMsg, wParam, lParam );
      return( FALSE );

    case WM_PAINT:
      hDC = BeginPaint( hWnd, &ps );
      GetClientRect( hWnd, &rect );

      SelectObject( hDC, GetStockObject(WHITE_PEN) );
      MoveToEx( hDC, rect.left, rect.top, NULL );
      LineTo( hDC, rect.right+1, rect.top );

      #define DKGRAY_PEN RGB(128, 128, 128)
      SelectObject( hDC, (HANDLE) CreatePen( PS_SOLID, 1, DKGRAY_PEN ) );
      MoveToEx( hDC, rect.left, rect.bottom-2, NULL );
      LineTo( hDC, rect.right+1, rect.bottom-2 );

      SelectObject( hDC, (HANDLE) CreatePen( PS_SOLID, 1, BLACK_PEN ) );
      MoveToEx( hDC, rect.left, rect.bottom-1, NULL );
      LineTo( hDC, rect.right+1, rect.bottom-1 );

      DeleteObject( SelectObject( hDC, GetStockObject(BLACK_PEN) ) );
      EndPaint( hWnd, &ps );
      return( FALSE );

    default:
      return( DefWindowProc(hWnd, uMsg, wParam, lParam) );
  }
}


TOOLBAR.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#ifndef TOOLBAR_H

 #define TOOLBAR_H

 #define TB_SPACE 0xFFFFFFFF

 //-- typedefs
 typedef struct TEXTBUTTON_STRUCT {
           LPTSTR lpButtonText;   // The string that appears on the button
           INT    idButton;       // This id is sent to the window proc
           HWND   hWndButton;     // The returned window handle of the button
         } TEXTBUTTON, *LPTEXTBUTTON;

 //-- prototypes
 HWND TextButtonBar( HWND, LPTEXTBUTTON, LPINT );

#endif // TOOLBAR_H


README.TXT

Creating a WinDbg Extension

SUMMARY
=======

The WDBGEXTS sample demonstrates how to create an extention for the WinDbg
debugger. This is a port of the sample demonstrating how to create an NTSD
extension.

Extensions are DLL entry points. The arguments passed to an extension are:

    HANDLE hCurrentProcess - Supplies a handle to the current process (at
        the time the extension is called).

    HANDLE hCurrentThread - Supplies a handle to the current thread (at
        the time the extension is called).

    DWORD CurrentPc - Supplies the current pc at the time the extension is
        called.

    PWINDBG_EXTENSION_APIS lpExtensionApis - Supplies the address of the
        functions callable by this extension.

    LPSTR lpArgumentString - Supplies the command-line arguments for the
        extension.

The type PWINDBG_EXTENSION_APIS is defined in \mstools\h\wdbgexts.h.

Note that in the makefile the -Gz compiler option is specified to ensure
that __stdcall is used.

MORE INFORMATION
================

The following is a description of the exported functions.

Function   Description

igrep      Searches the instruction stream for a pattern.
str        Given a pointer to a string, it prints out the string, its
           length, and its location in memory.

To use the commands contained in WDBGEXTS.DLL, make sure that the DLL is
placed in a directory which is on the PATH.

The syntax for the commands is as follows:

    !wdbgexts.igrep [pattern [expression] ]

    !wdbgexts.str [string]


WDBGEXTS.C

/**********************************************************************\
*                                                                      *
* Copyright 1992 - 1998 Microsoft Corporation                      *
*                                                                      *
* Module Name:                                                         *
*                                                                      *
*    wdbgexts.c                                                        *
*                                                                      *
* Abstract:                                                            *
*                                                                      *
* This file is a ported version of ntsdexts.c, supporting remote       *
* operations in WinDbg command window.       *
*                                                                      *
* By including wdbgexts.h, an extension has 4 more apis available,     *
* beyond those available to ntsd extensions:       *
*                                                                      *
*    PWINDBG_READ_PROCESS_MEMORY_ROUTINE lpReadProcessMemoryRoutine;   *
*    PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE lpWriteProcessMemoryRoutine; *
*    PWINDBG_GET_THREAD_CONTEXT_ROUTINE lpGetThreadContextRoutine;     *
*    PWINDBG_SET_THREAD_CONTEXT_ROUTINE lpSetThreadContextRoutine;     *
*       *
* The apis available to NTSD and WINDBG extensions are:        *
*       *
*    PNTSD_OUTPUT_ROUTINE lpOutputRoutine;       *
*    PNTSD_GET_EXPRESSION lpGetExpressionRoutine;       *
*    PNTSD_GET_SYMBOL lpGetSymbolRoutine;       *
*    PNTSD_DISASM lpDisasmRoutine;       *
*    PNTSD_CHECK_CONTROL_C lpCheckControlCRoutine;       *
*       *
*                                                                      *
\**********************************************************************/

#include <windows.h>
#include <string.h>
#include <windbgkd.h>
#include <ntsdexts.h>
#include <wdbgexts.h>

#define MAXLEN 80

CHAR  igrepLastPattern[256];
DWORD igrepSearchStartAddress;
DWORD igrepLastPc;

/**********************************************************************\
*                                                                      *
* igrep( )                                                             *
*                                                                      *
* Description:                                                         *
*                                                                      *
*    This function is called as a WINDBG extension to grep the         *
*    instruction stream for a particular pattern.                      *
*                                                                      *
* Arguments:                                                           *
*                                                                      *
*    hCurrentProcess - Supplies a handle to the current process (at    *
*        the time the extension was called).                           *
*                                                                      *
*    hCurrentThread - Supplies a handle to the current thread (at      *
*        the time the extension was called).                           *
*                                                                      *
*    CurrentPc - Supplies the current pc at the time the extension is  *
*        called.                                                       *
*                                                                      *
*    lpExtensionApis - Supplies the address of the functions callable  *
*        by this extension.                                            *
*                                                                      *
*    lpArgumentString   Supplies the pattern and expression for this   *
*        command.                                                      *
*                                                                      *
* Return Value:                                                        *
*                                                                      *
*    None.                                                             *
\**********************************************************************/

VOID igrep (
    HANDLE hCurrentProcess,
    HANDLE hCurrentThread,
    DWORD dwCurrentPc,
    PWINDBG_EXTENSION_APIS lpExtensionApis,
    LPSTR lpArgumentString
    )

{
    DWORD dwNextGrepAddr;
    DWORD dwCurrGrepAddr;
    CHAR SourceLine[256];
    BOOL NewPc;
    DWORD d;
    PNTSD_OUTPUT_ROUTINE lpOutputRoutine;
    PNTSD_GET_EXPRESSION lpGetExpressionRoutine;
    PNTSD_GET_SYMBOL lpGetSymbolRoutine;
    PNTSD_DISASM lpDisasmRoutine;
    PNTSD_CHECK_CONTROL_C lpCheckControlCRoutine;
    LPSTR pc;
    LPSTR Pattern;
    LPSTR Expression;
    CHAR Symbol[64];
    DWORD Displacement;

    UNREFERENCED_PARAMETER( hCurrentProcess );
    UNREFERENCED_PARAMETER( hCurrentThread );

    lpOutputRoutine = lpExtensionApis->lpOutputRoutine;
    lpGetExpressionRoutine = lpExtensionApis->lpGetExpressionRoutine;
    lpGetSymbolRoutine = lpExtensionApis->lpGetSymbolRoutine;
    lpDisasmRoutine = (PNTSD_DISASM) lpExtensionApis->lpDisasmRoutine;
    lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;

    if( igrepLastPc && igrepLastPc == dwCurrentPc ) {
        NewPc = FALSE;
    }
    else {
        igrepLastPc = dwCurrentPc;
        NewPc = TRUE;
    }

    //
    // check for pattern.
    //

    pc = lpArgumentString;
    Pattern = NULL;
    Expression = NULL;
    if( *pc ) {
        Pattern = pc;
        while( *pc > ' ' ) {
            pc++;
    }

        //
        // check for an expression
        //

        if( *pc != '\0' ) {
            *pc = '\0';
            pc++;
            if( *pc <= ' ' ) {
                while (*pc <= ' ') {
                    pc++;
                }
            }
            if( *pc ) {
                Expression = pc;
            }
        }
    }

    if( Pattern ) {
        strcpy(igrepLastPattern,Pattern);

        if( Expression ) {
            igrepSearchStartAddress = (lpGetExpressionRoutine)(Expression);
            if( !igrepSearchStartAddress ) {
                igrepSearchStartAddress = igrepLastPc;
                return;
            }
        }
        else {
            igrepSearchStartAddress = igrepLastPc;
        }
    }

    dwNextGrepAddr = igrepSearchStartAddress;
    dwCurrGrepAddr = dwNextGrepAddr;
    d = (lpDisasmRoutine)(&dwNextGrepAddr,SourceLine,FALSE);
    while( d ) {
        if( strstr(SourceLine,igrepLastPattern) ) {
            igrepSearchStartAddress = dwNextGrepAddr;
    (lpGetSymbolRoutine)((LPVOID)dwCurrGrepAddr,(PUCHAR)Symbol,&Displacement);
            (lpOutputRoutine)("%s",SourceLine);
            return;
        }
        if( (lpCheckControlCRoutine)() ) {
            return;
        }
        dwCurrGrepAddr = dwNextGrepAddr;
        d = (lpDisasmRoutine)(&dwNextGrepAddr,SourceLine,FALSE);
    }
}

/**********************************************************************\
*                                                                      *
* str( )       *
*                                                                      *
* Routine Description:                                                 *
*                                                                      *
*    This function is called as a WINDBG extension to format and dump  *
*    a counted ansi string.                                            *
*                                                                      *
* Arguments:                                                           *
*                                                                      *
*    hCurrentProcess - Supplies a handle to the current process (at    *
*        the time the extension was called).                           *
*                                                                      *
*    hCurrentThread - Supplies a handle to the current thread (at the  *
*        time the extension was called).                               *
*                                                                      *
*    CurrentPc - Supplies the current pc at the time the extension is  *
*        called.                                                       *
*                                                                      *
*    lpExtensionApis - Supplies the address of the functions callable  *
*        by this extension.                                            *
*                                                                      *
*    lpArgumentString - Supplies the asciiz string that describes the  *
*        ansi string to be dumped.                                     *
*                                                                      *
* Return Value:                                                        *
*                                                                      *
*    None.                                                             *
*                                                                      *
\**********************************************************************/

VOID str (
    HANDLE hCurrentProcess,
    HANDLE hCurrentThread,
    DWORD dwCurrentPc,
    PWINDBG_EXTENSION_APIS lpExtensionApis,
    LPSTR lpArgumentString
    )

{
    CHAR String[MAXLEN];
    size_t Length;
    DWORD dwAddrString;
    CHAR Symbol[64];
    LPSTR StringData;
    DWORD Displacement;
    BOOL b;
    PNTSD_OUTPUT_ROUTINE lpOutputRoutine;
    PNTSD_GET_EXPRESSION lpGetExpressionRoutine;
    PNTSD_GET_SYMBOL lpGetSymbolRoutine;

    UNREFERENCED_PARAMETER( hCurrentProcess );
    UNREFERENCED_PARAMETER( hCurrentThread );
    UNREFERENCED_PARAMETER( dwCurrentPc );

    lpOutputRoutine = lpExtensionApis->lpOutputRoutine;
    lpGetExpressionRoutine = lpExtensionApis->lpGetExpressionRoutine;
    lpGetSymbolRoutine = lpExtensionApis->lpGetSymbolRoutine;

    //
    // Evaluate the argument string to get the address of
    // the string to dump.
    //

    dwAddrString = (lpGetExpressionRoutine)(lpArgumentString);
    if (!dwAddrString) {
(lpOutputRoutine)( "Invalid Expression." );
return;
    }

    //
    // Get the symbolic name of the string
    //

    (lpGetSymbolRoutine)((LPVOID)dwAddrString,(PUCHAR)Symbol,&Displacement);

    //
    // Read current process memory and handle remote read as well
    //

    b = (lpExtensionApis->lpReadProcessMemoryRoutine)(
                                                     dwAddrString,
     String,
     MAXLEN,
                                                     NULL
                                                     );

    if (!b) {
(lpOutputRoutine)( "ReadProcessMemory failed." );
return;
    }

    Length = strlen( String );

    StringData = (LPSTR)LocalAlloc(LMEM_ZEROINIT,Length+1);

    if (!StringData) {
(lpOutputRoutine)( "LocalAlloc failed. Error = %x", GetLastError());
        return;
    }

    (lpOutputRoutine)(
"String: %s ; %d bytes at %lx\n",
String,
Length,
dwAddrString
        );

    LocalFree( StringData );
}


WINDBGKD.H

/*++ BUILD Version: 0045    // Increment this if a change has global effects

Copyright 1990 - 1998 Microsoft Corporation

Module Name:

    windbgkd.h

Abstract:

    This module defines the 32-Bit Windows Kernel Debugger interface.

Revision History:

--*/

#ifndef _WINDBGKD_
#define _WINDBGKD_

typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;

#ifdef _X86_

//
// DBGKD_CONTROL_REPORT
//
// This structure contains machine specific data passed to the debugger
// when a Wait_State_Change message is sent.  Idea is to allow debugger
// to do what it needes without reading any more packets.
// Structure is filled in by KdpSetControlReport
//

#define DBGKD_MAXSTREAM 16

typedef struct _DBGKD_CONTROL_REPORT {
    DWORD   Dr6;
    DWORD   Dr7;
    WORD    InstructionCount;
    WORD    ReportFlags;
    BYTE    InstructionStream[DBGKD_MAXSTREAM];
    WORD    SegCs;
    WORD    SegDs;
    WORD    SegEs;
    WORD    SegFs;
    DWORD   EFlags;
} DBGKD_CONTROL_REPORT, *PDBGKD_CONTROL_REPORT;

#define REPORT_INCLUDES_SEGS    0x0001  // this is for backward compatibility

//
// DBGKD_CONTROL_SET
//
// This structure control value the debugger wants to set on every
// continue, and thus sets here to avoid packet traffic.
//

typedef struct _DBGKD_CONTROL_SET {
    DWORD   TraceFlag;                  // WARNING: This must NOT be a BOOLEAN,
                                        //     or host and target will end
                                        //     up with different alignments!
    DWORD   Dr7;
    DWORD   CurrentSymbolStart;         // Range in which to trace locally
    DWORD   CurrentSymbolEnd;
} DBGKD_CONTROL_SET, *PDBGKD_CONTROL_SET;

#endif //_X86_

typedef struct _DESCRIPTOR_TABLE_ENTRY {
    DWORD Selector;
    LDT_ENTRY Descriptor;
} DESCRIPTOR_TABLE_ENTRY, *PDESCRIPTOR_TABLE_ENTRY;

#if defined(_MIPS_) | defined(_ALPHA_) | defined(_PPC_)

//
// Define RISC specific kernel debugger information.
//
// The following structure contains machine specific data passed to
// the host system kernel debugger in a wait state change message.
//

#define DBGKD_MAXSTREAM 16

typedef struct _DBGKD_CONTROL_REPORT {
    DWORD InstructionCount;
    BYTE  InstructionStream[DBGKD_MAXSTREAM];
} DBGKD_CONTROL_REPORT, *PDBGKD_CONTROL_REPORT;

//
// The following structure contains information that the host system
// kernel debugger wants to set on every continue operation and avoids
// the need to send extra packets of information.
//

typedef DWORD DBGKD_CONTROL_SET, *PDBGKD_CONTROL_SET;

#endif                          // RISC

#ifdef _X86_
//
// Special Registers for i386
//

typedef struct _DESCRIPTOR {
    WORD    Pad;
    WORD    Limit;
    DWORD   Base;
} KDESCRIPTOR, *PKDESCRIPTOR;

typedef struct _KSPECIAL_REGISTERS {
    DWORD Cr0;
    DWORD Cr2;
    DWORD Cr3;
    DWORD Cr4;
    DWORD KernelDr0;
    DWORD KernelDr1;
    DWORD KernelDr2;
    DWORD KernelDr3;
    DWORD KernelDr6;
    DWORD KernelDr7;
    KDESCRIPTOR Gdtr;
    KDESCRIPTOR Idtr;
    WORD   Tr;
    WORD   Ldtr;
    DWORD Reserved[6];
} KSPECIAL_REGISTERS, *PKSPECIAL_REGISTERS;

//
// Processor State frame: Before a processor freezes itself, it
// dumps the processor state to the processor state frame for
// debugger to examine.
//

typedef struct _KPROCESSOR_STATE {
    struct _CONTEXT ContextFrame;
    struct _KSPECIAL_REGISTERS SpecialRegisters;
} KPROCESSOR_STATE, *PKPROCESSOR_STATE;
#endif // _X86_

typedef struct _DBGKM_EXCEPTION {
    EXCEPTION_RECORD ExceptionRecord;
    DWORD FirstChance;
} DBGKM_EXCEPTION, *PDBGKM_EXCEPTION;

//
// DbgKd APIs are for the portable kernel debugger
//

//
// KD_PACKETS are the low level data format used in KD. All packets
// begin with a packet leader, byte count, packet type. The sequence
// for accepting a packet is:
//
//  - read 4 bytes to get packet leader.  If read times out (10 seconds)
//    with a short read, or if packet leader is incorrect, then retry
//    the read.
//
//  - next read 2 byte packet type.  If read times out (10 seconds) with
//    a short read, or if packet type is bad, then start again looking
//    for a packet leader.
//
//  - next read 4 byte packet Id.  If read times out (10 seconds)
//    with a short read, or if packet Id is not what we expect, then
//    ask for resend and restart again looking for a packet leader.
//
//  - next read 2 byte byte count.  If read times out (10 seconds) with
//    a short read, or if byte count is greater than PACKET_MAX_SIZE,
//    then start again looking for a packet leader.
//
//  - next read 4 byte packet data checksum.
//
//  - The packet data immediately follows the packet.  There should be
//    ByteCount bytes following the packet header.  Read the packet
//    data, if read times out (10 seconds) then start again looking for
//    a packet leader.
//

typedef struct _KD_PACKET {
    DWORD PacketLeader;
    WORD   PacketType;
    WORD   ByteCount;
    DWORD PacketId;
    DWORD Checksum;
} KD_PACKET, *PKD_PACKET;

#define PACKET_MAX_SIZE 4000
#define INITIAL_PACKET_ID 0x80800000    // DON't use 0
#define SYNC_PACKET_ID    0x00000800    // Or in with INITIAL_PACKET_ID
                                        // to force a packet ID reset.

//
// BreakIn packet
//

#define BREAKIN_PACKET                  0x62626262
#define BREAKIN_PACKET_BYTE             0x62

//
// Packet lead in sequence
//

#define PACKET_LEADER                   0x30303030 //0x77000077
#define PACKET_LEADER_BYTE              0x30

#define CONTROL_PACKET_LEADER           0x69696969
#define CONTROL_PACKET_LEADER_BYTE      0x69

//
// Packet Trailing Byte
//

#define PACKET_TRAILING_BYTE            0xAA

//
// Packet Types
//

#define PACKET_TYPE_UNUSED              0
#define PACKET_TYPE_KD_STATE_CHANGE     1
#define PACKET_TYPE_KD_STATE_MANIPULATE 2
#define PACKET_TYPE_KD_DEBUG_IO         3
#define PACKET_TYPE_KD_ACKNOWLEDGE      4       // Packet-control type
#define PACKET_TYPE_KD_RESEND           5       // Packet-control type
#define PACKET_TYPE_KD_RESET            6       // Packet-control type
#define PACKET_TYPE_MAX                 7

//
// If the packet type is PACKET_TYPE_KD_STATE_CHANGE, then
// the format of the packet data is as follows:
//

#define DbgKdExceptionStateChange   0x00003030L
#define DbgKdLoadSymbolsStateChange 0x00003031L

//
// Pathname Data follows directly
//

typedef struct _DBGKD_LOAD_SYMBOLS {
    DWORD PathNameLength;
    PVOID BaseOfDll;
    DWORD ProcessId;
    DWORD CheckSum;
    DWORD SizeOfImage;
    BOOLEAN UnloadSymbols;
} DBGKD_LOAD_SYMBOLS, *PDBGKD_LOAD_SYMBOLS;

typedef struct _DBGKD_WAIT_STATE_CHANGE {
    DWORD NewState;
    WORD   ProcessorLevel;
    WORD   Processor;
    DWORD NumberProcessors;
    PVOID Thread;
    PVOID ProgramCounter;
    union {
        DBGKM_EXCEPTION Exception;
        DBGKD_LOAD_SYMBOLS LoadSymbols;
    } u;
    DBGKD_CONTROL_REPORT ControlReport;
    CONTEXT Context;
} DBGKD_WAIT_STATE_CHANGE, *PDBGKD_WAIT_STATE_CHANGE;

//
// If the packet type is PACKET_TYPE_KD_STATE_MANIPULATE, then
// the format of the packet data is as follows:
//
// Api Numbers for state manipulation
//

#define DbgKdReadVirtualMemoryApi     0x00003130L
#define DbgKdWriteVirtualMemoryApi    0x00003131L
#define DbgKdGetContextApi            0x00003132L
#define DbgKdSetContextApi            0x00003133L
#define DbgKdWriteBreakPointApi       0x00003134L
#define DbgKdRestoreBreakPointApi     0x00003135L
#define DbgKdContinueApi              0x00003136L
#define DbgKdReadControlSpaceApi      0x00003137L
#define DbgKdWriteControlSpaceApi     0x00003138L
#define DbgKdReadIoSpaceApi           0x00003139L
#define DbgKdWriteIoSpaceApi          0x0000313AL
#define DbgKdRebootApi                0x0000313BL
#define DbgKdContinueApi2             0x0000313CL
#define DbgKdReadPhysicalMemoryApi    0x0000313DL
#define DbgKdWritePhysicalMemoryApi   0x0000313EL
#define DbgKdQuerySpecialCallsApi     0x0000313FL
#define DbgKdSetSpecialCallApi        0x00003140L
#define DbgKdClearSpecialCallsApi     0x00003141L
#define DbgKdSetInternalBreakPointApi 0x00003142L
#define DbgKdGetInternalBreakPointApi 0x00003143L
#define DbgKdReadIoSpaceExtendedApi   0x00003144L
#define DbgKdWriteIoSpaceExtendedApi  0x00003145L
#define DbgKdGetVersionApi            0x00003146L
#define DbgKdWriteBreakPointExApi     0x00003147L
#define DbgKdRestoreBreakPointExApi   0x00003148L
#define DbgKdCauseBugCheckApi         0x00003149L
#define DbgKdSwitchProcessor          0x00003150L

//
// Response is a read memory message with data following
//

typedef struct _DBGKD_READ_MEMORY {
    PVOID TargetBaseAddress;
    DWORD TransferCount;
    DWORD ActualBytesRead;
} DBGKD_READ_MEMORY, *PDBGKD_READ_MEMORY;

//
// Data follows directly
//

typedef struct _DBGKD_WRITE_MEMORY {
    PVOID TargetBaseAddress;
    DWORD TransferCount;
    DWORD ActualBytesWritten;
} DBGKD_WRITE_MEMORY, *PDBGKD_WRITE_MEMORY;

//
// Response is a get context message with a full context record following
//

typedef struct _DBGKD_GET_CONTEXT {
    DWORD ContextFlags;
} DBGKD_GET_CONTEXT, *PDBGKD_GET_CONTEXT;

//
// Full Context record follows
//

typedef struct _DBGKD_SET_CONTEXT {
    DWORD ContextFlags;
} DBGKD_SET_CONTEXT, *PDBGKD_SET_CONTEXT;

typedef struct _DBGKD_WRITE_BREAKPOINT {
    PVOID BreakPointAddress;
    DWORD BreakPointHandle;
} DBGKD_WRITE_BREAKPOINT, *PDBGKD_WRITE_BREAKPOINT;

typedef struct _DBGKD_RESTORE_BREAKPOINT {
    DWORD BreakPointHandle;
} DBGKD_RESTORE_BREAKPOINT, *PDBGKD_RESTORE_BREAKPOINT;

typedef struct _DBGKD_BREAKPOINTEX {
    DWORD     BreakPointCount;
    DWORD     ContinueStatus;
} DBGKD_BREAKPOINTEX, *PDBGKD_BREAKPOINTEX;

typedef struct _DBGKD_CONTINUE {
    DWORD    ContinueStatus;
} DBGKD_CONTINUE, *PDBGKD_CONTINUE;

typedef struct _DBGKD_CONTINUE2 {
    DWORD    ContinueStatus;
    DBGKD_CONTROL_SET ControlSet;
} DBGKD_CONTINUE2, *PDBGKD_CONTINUE2;

typedef struct _DBGKD_READ_WRITE_IO {
    DWORD DataSize;                     // 1, 2, 4
    PVOID IoAddress;
    DWORD DataValue;
} DBGKD_READ_WRITE_IO, *PDBGKD_READ_WRITE_IO;

typedef struct _DBGKD_READ_WRITE_IO_EXTENDED {
    DWORD DataSize;                     // 1, 2, 4
    DWORD InterfaceType;
    DWORD BusNumber;
    DWORD AddressSpace;
    PVOID IoAddress;
    DWORD DataValue;
} DBGKD_READ_WRITE_IO_EXTENDED, *PDBGKD_READ_WRITE_IO_EXTENDED;

typedef struct _DBGKD_QUERY_SPECIAL_CALLS {
    DWORD NumberOfSpecialCalls;
    // DWORD SpecialCalls[];
} DBGKD_QUERY_SPECIAL_CALLS, *PDBGKD_QUERY_SPECIAL_CALLS;

typedef struct _DBGKD_SET_SPECIAL_CALL {
    DWORD SpecialCall;
} DBGKD_SET_SPECIAL_CALL, *PDBGKD_SET_SPECIAL_CALL;

typedef struct _DBGKD_SET_INTERNAL_BREAKPOINT {
    DWORD BreakpointAddress;
    DWORD Flags;
} DBGKD_SET_INTERNAL_BREAKPOINT, *PDBGKD_SET_INTERNAL_BREAKPOINT;

typedef struct _DBGKD_GET_INTERNAL_BREAKPOINT {
    DWORD BreakpointAddress;
    DWORD Flags;
    DWORD Calls;
    DWORD MaxCallsPerPeriod;
    DWORD MinInstructions;
    DWORD MaxInstructions;
    DWORD TotalInstructions;
} DBGKD_GET_INTERNAL_BREAKPOINT, *PDBGKD_GET_INTERNAL_BREAKPOINT;

#define DBGKD_INTERNAL_BP_FLAG_COUNTONLY 0x00000001 // don't count instructions
#define DBGKD_INTERNAL_BP_FLAG_INVALID   0x00000002 // disabled BP
#define DBGKD_INTERNAL_BP_FLAG_SUSPENDED 0x00000004 // temporarily suspended
#define DBGKD_INTERNAL_BP_FLAG_DYING     0x00000008 // kill on exit

typedef struct _DBGKD_GET_VERSION {
    WORD    MajorVersion;
    WORD    MinorVersion;
    WORD    ProtocolVersion;
    WORD    Flags;
    DWORD   KernBase;
    DWORD   PsLoadedModuleList;
} DBGKD_GET_VERSION, *PDBGKD_GET_VERSION;

#define DBGKD_VERS_FLAG_MP      0x0001      // kernel is MP built

typedef struct _DBGKD_MANIPULATE_STATE {
    DWORD ApiNumber;
    WORD   ProcessorLevel;
    WORD   Processor;
    DWORD    ReturnStatus;
    union {
        DBGKD_READ_MEMORY ReadMemory;
        DBGKD_WRITE_MEMORY WriteMemory;
        DBGKD_GET_CONTEXT GetContext;
        DBGKD_SET_CONTEXT SetContext;
        DBGKD_WRITE_BREAKPOINT WriteBreakPoint;
        DBGKD_RESTORE_BREAKPOINT RestoreBreakPoint;
        DBGKD_CONTINUE Continue;
        DBGKD_CONTINUE2 Continue2;
        DBGKD_READ_WRITE_IO ReadWriteIo;
        DBGKD_READ_WRITE_IO_EXTENDED ReadWriteIoExtended;
        DBGKD_QUERY_SPECIAL_CALLS QuerySpecialCalls;
        DBGKD_SET_SPECIAL_CALL SetSpecialCall;
        DBGKD_SET_INTERNAL_BREAKPOINT SetInternalBreakpoint;
        DBGKD_GET_INTERNAL_BREAKPOINT GetInternalBreakpoint;
        DBGKD_GET_VERSION GetVersion;
        DBGKD_BREAKPOINTEX BreakPointEx;
    } u;
} DBGKD_MANIPULATE_STATE, *PDBGKD_MANIPULATE_STATE;

//
// This is the format for the trace data passed back from the kernel to
// the debugger to describe multiple calls that have returned since the
// last trip back.  The basic format is that there are a bunch of these
// (4 byte) unions stuck together.  Each union is of one of two types: a
// 4 byte unsigned long interger, or a three field struct, describing a
// call (where "call" is delimited by returning or exiting the symbol
// scope).  If the number of instructions executed is too big to fit
// into a WORD   -1, then the Instructions field has
// TRACE_DATA_INSTRUCTIONS_BIG and the next union is a LongNumber
// containing the real number of instructions executed.
//
// The very first union returned in each callback is a LongNumber
// containing the number of unions returned (including the "size"
// record, os it's always at least 1 even if there's no data to return).
//
// This is all returned to the debugger when one of two things
// happens:
//
//   1) The pc moves out of all defined symbol ranges
//   2) The buffer of trace data entries is filled.
//
// The "trace done" case is hacked around on the debugger side.  It
// guarantees that the pc address that indicates a trace exit never
// winds up in a defined symbol range.
//
// The only other complexity in this system is handling the SymbolNumber
// table.  This table is kept in parallel by the kernel and the
// debugger.  When the PC exits a known symbol range, the Begin and End
// symbol ranges are set by the debugger and are allocated to the next
// symbol slot upon return.  "The next symbol slot" means the numerical
// next slot number, unless we've filled all slots, in which case it is
// #0.  (ie., allocation is cyclic and not LRU or something).  The
// SymbolNumber table is flushed when a SpecialCalls call is made (ie.,
// at the beginning of the WatchTrace).
//

typedef union _DBGKD_TRACE_DATA {
    struct {
        BYTE  SymbolNumber;
        CHAR LevelChange;
        WORD   Instructions;
    } s;
    DWORD LongNumber;
} DBGKD_TRACE_DATA, *PDBGKD_TRACE_DATA;

#define TRACE_DATA_INSTRUCTIONS_BIG 0xffff

#define TRACE_DATA_BUFFER_MAX_SIZE 40

//
// If the packet type is PACKET_TYPE_KD_DEBUG_IO, then
// the format of the packet data is as follows:
//

#define DbgKdPrintStringApi     0x00003230L
#define DbgKdGetStringApi       0x00003231L

//
// For print string, the Null terminated string to print
// immediately follows the message
//
typedef struct _DBGKD_PRINT_STRING {
    DWORD LengthOfString;
} DBGKD_PRINT_STRING, *PDBGKD_PRINT_STRING;

//
// For get string, the Null terminated promt string
// immediately follows the message. The LengthOfStringRead
// field initially contains the maximum number of characters
// to read. Upon reply, this contains the number of bytes actually
// read. The data read immediately follows the message.
//
//
typedef struct _DBGKD_GET_STRING {
    DWORD LengthOfPromptString;
    DWORD LengthOfStringRead;
} DBGKD_GET_STRING, *PDBGKD_GET_STRING;

typedef struct _DBGKD_DEBUG_IO {
    DWORD ApiNumber;
    WORD   ProcessorLevel;
    WORD   Processor;
    union {
        DBGKD_PRINT_STRING PrintString;
        DBGKD_GET_STRING GetString;
    } u;
} DBGKD_DEBUG_IO, *PDBGKD_DEBUG_IO;

VOID
NTAPI
DbgKdSendBreakIn(
    VOID
    );

PBYTE
NTAPI
DbgKdGets(
    PBYTE  Buffer,
    WORD   Length
    );

DWORD
NTAPI
DbgKdWaitStateChange(
    OUT PDBGKD_WAIT_STATE_CHANGE StateChange,
    OUT PVOID Buffer,
    IN DWORD BufferLength
    );

DWORD
NTAPI
DbgKdContinue (
    IN DWORD    ContinueStatus
    );

DWORD
NTAPI
DbgKdContinue2 (
    IN DWORD    ContinueStatus,
    IN DBGKD_CONTROL_SET ControlSet
    );

DWORD
NTAPI
DbgKdReadVirtualMemory(
    IN PVOID TargetBaseAddress,
    OUT PVOID UserInterfaceBuffer,
    IN DWORD TransferCount,
    OUT PDWORD ActualBytesRead OPTIONAL
    );

DWORD
NTAPI
DbgKdWriteVirtualMemory(
    IN PVOID TargetBaseAddress,
    OUT PVOID UserInterfaceBuffer,
    IN DWORD TransferCount,
    OUT PDWORD ActualBytesWritten OPTIONAL
    );

DWORD
NTAPI
DbgKdReadPhysicalMemory(
    IN PHYSICAL_ADDRESS TargetBaseAddress,
    OUT PVOID UserInterfaceBuffer,
    IN DWORD TransferCount,
    OUT PDWORD ActualBytesRead OPTIONAL
    );

DWORD
NTAPI
DbgKdWritePhysicalMemory(
    IN PHYSICAL_ADDRESS TargetBaseAddress,
    OUT PVOID UserInterfaceBuffer,
    IN DWORD TransferCount,
    OUT PDWORD ActualBytesWritten OPTIONAL
    );

DWORD
NTAPI
DbgKdReadControlSpace(
    IN WORD   Processor,
    IN PVOID TargetBaseAddress,
    OUT PVOID UserInterfaceBuffer,
    IN DWORD TransferCount,
    OUT PDWORD ActualBytesRead OPTIONAL
    );

DWORD
NTAPI
DbgKdWriteControlSpace(
    IN WORD   Processor,
    IN PVOID TargetBaseAddress,
    OUT PVOID UserInterfaceBuffer,
    IN DWORD TransferCount,
    OUT PDWORD ActualBytesWritten OPTIONAL
    );

DWORD
NTAPI
DbgKdReadIoSpace(
    IN PVOID IoAddress,
    OUT PVOID ReturnedData,
    IN DWORD DataSize
    );

DWORD
NTAPI
DbgKdWriteIoSpace(
    IN PVOID IoAddress,
    IN DWORD DataValue,
    IN DWORD DataSize
    );

DWORD
NTAPI
DbgKdGetContext(
    IN WORD   Processor,
    IN OUT PCONTEXT Context
    );

DWORD
NTAPI
DbgKdSetContext(
    IN WORD   Processor,
    IN PCONTEXT Context
    );

DWORD
NTAPI
DbgKdWriteBreakPoint(
    IN PVOID BreakPointAddress,
    OUT PDWORD BreakPointHandle
    );

DWORD
NTAPI
DbgKdRestoreBreakPoint(
    IN DWORD BreakPointHandle
    );

DWORD
NTAPI
DbgKdReboot(
    VOID
    );

#ifdef _X86_
DWORD
NTAPI
DbgKdLookupSelector(
    IN WORD   Processor,
    IN OUT PDESCRIPTOR_TABLE_ENTRY pDescriptorTableEntry
    );
#endif

#define DBG_EXCEPTION_HANDLED           ((DWORD   )0x00010001L)

#endif // _WINDBGKD_


FIBERS.C

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    fibers.c

Abstract:

    This module illustrates the Win32 fiber APIs.

    This example implements a fiber based file copy operation.  Note that
    a number of different techiques exists for copying files programmatically;
    this sample simply illustrates the fiber APIs.

    This example makes use of a fiber data structure which is used to
    determine the behavior and state of the fiber.  One data structure
    exists for each fiber; the pointer to the data structure is passed
    to the fiber at fiber creation time via the lpParameter fiber parameter.

    The executing thread in the process makes a call to ConvertThreadToFiber,
    which allows fibers to be scheduled by the caller.  This also allows
    the resultant fiber to be scheduled by another fiber.

    Next, two additional fibers are created, one fiber which performs read
    operations against a specified file, and another fiber which performs
    the write operations against a specified file.

    The primary fiber then schedules the read fiber.  After a succesful
    read, the read fiber schedules the write fiber.  After a succesful write
    in the write fiber, the write fiber schedules the read fiber.  When
    the read/write cycle has completed, the primary fiber is scheduled,
    which results in the display of the read/write status. If an error occurs
    during the read or write fibers, the primary fiber is scheduled and status
    of the read/write is displayed.

    The fibers, fiber data structures, and file handles are then freed
    prior to process termination.

Author:

    Scott Field (sfield) 30-Nov-95

Revision History:

--*/

#include <windows.h>
#include <stdio.h>

VOID
__stdcall
ReadFiberFunc(
    LPVOID lpParameter
    );

VOID
__stdcall
WriteFiberFunc(
    LPVOID lpParameter
    );

void
DisplayFiberInfo(
    void
    );

typedef struct {
    DWORD dwParameter;          // DWORD parameter to fiber (unused)
    DWORD dwFiberResultCode;    // GetLastError() result code
    HANDLE hFile;               // handle to operate on
    DWORD dwBytesProcessed;     // number of bytes processed
} FIBERDATASTRUCT, *PFIBERDATASTRUCT, *LPFIBERDATASTRUCT;

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

#define BUFFER_SIZE 32768   // read/write buffer size
#define FIBER_COUNT 3       // total number of fibers (including primary)

#define PRIMARY_FIBER 0 // array index to primary fiber
#define READ_FIBER 1    // array index to read fiber
#define WRITE_FIBER 2   // array index to write fiber

LPVOID g_lpFiber[FIBER_COUNT];
LPBYTE g_lpBuffer;
DWORD g_dwBytesRead;

int
__cdecl
main(
    int argc,
    char *argv[]
    )
{
    LPFIBERDATASTRUCT fs;

    if(argc != 3) {
        printf("Usage: %s <SourceFile> <DestinationFile>\n", argv[0]);
        return RTN_USAGE;
    }

    //
    // allocate storage for our fiber data structures
    //
    fs = (LPFIBERDATASTRUCT)HeapAlloc(
        GetProcessHeap(), 0, sizeof(FIBERDATASTRUCT) * FIBER_COUNT);

    if(fs == NULL) {
        printf("HeapAlloc error! (rc%=lu)\n", GetLastError());
        return RTN_ERROR;
    }

    //
    // allocate storage for the read/write buffer
    //
    g_lpBuffer = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE);
    if(g_lpBuffer == NULL) {
        printf("HeapAlloc error! (rc=%lu)\n", GetLastError());
        return RTN_ERROR;
    }

    //
    // open the source file
    //
    fs[READ_FIBER].hFile = CreateFile(
        argv[1],
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL
        );

    if(fs[READ_FIBER].hFile == INVALID_HANDLE_VALUE) {
        printf("CreateFile error! (rc=%lu)\n", GetLastError());
        return RTN_ERROR;
    }

    //
    // open the destination file
    //
    fs[WRITE_FIBER].hFile = CreateFile(
        argv[2],
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_NEW,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL
        );

    if(fs[WRITE_FIBER].hFile == INVALID_HANDLE_VALUE) {
        printf("CreateFile error! (rc=%lu)\n", GetLastError());
        return RTN_ERROR;
    }

    //
    // convert this thread to a fiber, to allow scheduling of other fibers
    //
    g_lpFiber[PRIMARY_FIBER] = ConvertThreadToFiber(&fs[PRIMARY_FIBER]);

    if(g_lpFiber[PRIMARY_FIBER] == NULL) {
        printf("ConvertThreadToFiber failed! rc=%lu\n", GetLastError());
        return RTN_ERROR;
    }

    //
    // Just initialize the primary fiber data structure.  We don't use
    // the primary fiber data structure for anything in this sample.
    //
    fs[PRIMARY_FIBER].dwParameter = 0;
    fs[PRIMARY_FIBER].dwFiberResultCode = 0;
    fs[PRIMARY_FIBER].hFile = INVALID_HANDLE_VALUE;

    //
    // create the Read fiber
    //
    g_lpFiber[READ_FIBER] = CreateFiber(0, ReadFiberFunc, &fs[READ_FIBER]);

    if(g_lpFiber[READ_FIBER] == NULL) {
        printf("CreateFiber error! (rc=%lu)\n", GetLastError());
        return RTN_ERROR;
    }

    fs[READ_FIBER].dwParameter = 0x12345678;

    //
    // create the Write fiber
    //
    g_lpFiber[WRITE_FIBER]=CreateFiber(0, WriteFiberFunc, &fs[WRITE_FIBER]);

    if(g_lpFiber[WRITE_FIBER] == NULL) {
        printf("CreateFiber error! (rc=%lu)\n", GetLastError());
        return RTN_ERROR;
    }

    fs[WRITE_FIBER].dwParameter = 0x54545454;

    //
    // switch to the read fiber
    //
    SwitchToFiber(g_lpFiber[READ_FIBER]);

    //
    // We have now been scheduled again.  Display results from the read/write
    // fibers
    //
    printf("ReadFiber result == %lu Bytes Processed == %lu\n",
        fs[READ_FIBER].dwFiberResultCode, fs[READ_FIBER].dwBytesProcessed);

    printf("WriteFiber result == %lu Bytes Processed == %lu\n",
        fs[WRITE_FIBER].dwFiberResultCode, fs[WRITE_FIBER].dwBytesProcessed);

    //
    // Delete the fibers
    //
    DeleteFiber(g_lpFiber[READ_FIBER]);
    DeleteFiber(g_lpFiber[WRITE_FIBER]);

    //
    // close handles
    //
    CloseHandle(fs[READ_FIBER].hFile);
    CloseHandle(fs[WRITE_FIBER].hFile);

    //
    // free allocated memory
    //
    HeapFree(GetProcessHeap(), 0, g_lpBuffer);
    HeapFree(GetProcessHeap(), 0, fs);

    return RTN_OK;
}

VOID
__stdcall
ReadFiberFunc(
    LPVOID lpParameter
    )
{
    LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)lpParameter;

    //
    // if this fiber was passed NULL for fiber data, just return,
    // causing the current thread to exit
    //
    if(fds == NULL) {
        printf("Passed NULL fiber data.  Exiting current thread.\n");
        return;
    }

    //
    // display some information pertaining to the current fiber
    //
    DisplayFiberInfo();

    fds->dwBytesProcessed = 0;

    while(1) {
        //
        // read data from file specified in the READ_FIBER data structure
        //
        if(!ReadFile(fds->hFile, g_lpBuffer, BUFFER_SIZE, &g_dwBytesRead, NULL)) {
            break;
        }

        //
        // if we reached EOF, break
        //
        if(g_dwBytesRead == 0) break;

        //
        // update number of bytes processed in the fiber data structure
        //
        fds->dwBytesProcessed += g_dwBytesRead;

        //
        // switch to the write fiber
        //
        SwitchToFiber(g_lpFiber[WRITE_FIBER]);
    } // while

    //
    // update the fiber result code
    //
    fds->dwFiberResultCode = GetLastError();

    //
    // switch back to the primary fiber
    //
    SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}

VOID
__stdcall
WriteFiberFunc(
    LPVOID lpParameter
    )
{
    LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)lpParameter;
    DWORD dwBytesWritten;

    //
    // if this fiber was passed NULL for fiber data, just return,
    // causing the current thread to exit
    //
    if(fds == NULL) {
        printf("Passed NULL fiber data.  Exiting current thread.\n");
        return;
    }

    //
    // display some information pertaining to the current fiber
    //
    DisplayFiberInfo();

    //
    // assume all of the writes succeed.  If a write fails, the fiber
    // result code will be updated to reflect the reason for failure
    //
    fds->dwBytesProcessed = 0;
    fds->dwFiberResultCode = ERROR_SUCCESS;

    while (1) {
        //
        // write data to the file specified in the WRITE_FIBER data structure
        //
        if(!WriteFile(fds->hFile, g_lpBuffer, g_dwBytesRead, &dwBytesWritten, NULL)) {
            //
            // if an error occurred writing, break
            //
            break;
        }

        //
        // update number of bytes processed in the fiber data structure
        //
        fds->dwBytesProcessed += dwBytesWritten;

        //
        // switch back to the read fiber
        //
        SwitchToFiber(g_lpFiber[READ_FIBER]);
    }  // while

    //
    // if an error occurred, update the fiber result code...
    //
    fds->dwFiberResultCode = GetLastError();

    //
    // ...and switch to the primary fiber
    //
    SwitchToFiber(g_lpFiber[PRIMARY_FIBER]);
}

void
DisplayFiberInfo(
    void
    )
{
    LPFIBERDATASTRUCT fds = (LPFIBERDATASTRUCT)GetFiberData();
    LPVOID lpCurrentFiber = GetCurrentFiber();

    //
    // determine which fiber we are executing, based on the fiber address
    //
    if(lpCurrentFiber == g_lpFiber[READ_FIBER])
        printf("Read Fiber entered");
    else {
        if(lpCurrentFiber == g_lpFiber[WRITE_FIBER])
            printf("Write Fiber entered");
        else {
            if(lpCurrentFiber == g_lpFiber[PRIMARY_FIBER])
                printf("Primary Fiber entered");
            else
                printf("Unknown Fiber entered");
            }
    }

    //
    // display dwParameter from the current fiber data structure
    //
    printf(" (dwParameter == 0x%lx)\n", fds->dwParameter);
}


README.TXT

Pattern Matching Search

SUMMARY
=======

The PDC sample is a character mode program for searching the files in a
directory tree for a match against a pattern. It uses multiple threads, with
each thread processing a file at a time, accumulating its matches and
outputting them to standard output contiguously when it is done searching
a file.

MORE INFORMATION
================

This program demonstrates how to use many of the advanced operating system
features provided by the Win32 API. The features that this program
demonstrates are:

  - Creating multiple threads, using synchronization objects
  - Thread termination
  - Virtual memory commitment versus reservation
  - Structured exception handling, using an exception filter procedure
  - Enumeration of directory entries
  - File mapping
  - Asynchronous file I/O using a completion routine
  - Synchronous file I/O

The command-line syntax is:

PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]

where:
    h - Prints this message.
    v - Generates verbose output.
    y - Ignores case when doing comparisons.
    a - Specifies that the program should use asynchronous file I/O to read
        the files being searched.
    s - Specifies that the program should use synchronous file I/O to read
        the files being searched.
    m - Specifies that the program should use mapped file I/O to read the
        files being searched.
    t - Specifies the number of threads to use when doing the search.
        Default is 4 * the number of processors.
    SearchString - Specifies the text to search for. Enclose in quotes if it
        contains spaces or punctuation.
    DirectoryPath - Specifies the root of the tree to begin the search at.
        Defaults to the current directory.


PDC.C

/*++

Copyright 1992 - 1998 Microsoft Corporation

Module Name:

    pdc.c

Abstract:

    This is the main source file for the Windows/NT PDC API demonstration
    program.  This program demonstrates how to use many of the advanced
    operating system features provided by the Win32 API set on Windows/NT.

    This file and its corresponding header file, pdc.h, can be found
    in the sample programs directory of the PDC CD-ROM.

    This program has a real purpose, although the implementation is
    somewhat contrived in order to demonstrate the various operating
    system features of Windows/NT.

    The features that this program demonstrate are:

        - Creating multiple threads, using critical sections
          and semaphores for synchronization.

        - Thread termination.

        - Virtual memory, commitment vs reservation.

        - Structured exception handling, including finally
          clauses and an exception filter procedure.

        - Enumeration of directory entries.

        - Mapped file I/O

        - Asynchronous file I/O via completion routine.

        - Synchronous file I/O

    PDC is a character mode program for searching the files in a
    directory tree for a match against a pattern.  It uses multiple
    threads to do it's work, with each thread processing a file at a
    time, accumulating it's matches and outputting them to standard
    output contiguously when it is done searching a file.

    The command line syntax is:

        Usage: PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]

        where:

            -h - prints this message.

            -v - generates verbose output.

            -y - ignores case when doing comparisons.

            -a - specifies that the program should use asynchronous file
                 I/O to read the files being searched.

            -s - specifies that the program should use synchronous file
                 I/O to read the files being searched.

            -m - specifies that the program should use mapped file I/O
                 to read the files being searched.

            -t - specifies the number of threads to use when doing the
                 search.  Default is 4 * the number of processors.

            SearchString - specifies the text to search for.  Enclose in
                           quotes if it contains spaces or punctuation.

            DirectoryPath - specifies the root of the tree to begin the
                            search at.  Defaults to the current directory.

--*/

#include "pdc.h"

int
main(
    int argc,
    char *argv[]
    )

/*++

Routine Description:

    This is the main procedure for the PDC program, and is called by
    the C Runtime startup code when the program starts.

Arguments:

    argc - number of argumments in the argv array.

    argv - pointer to an array of null terminated string pointers.

Return Value:

    Process exit status.  The value returned by this function will
    be used as the exit code parameter passed to ExitProcess.

--*/

{
    SYSTEM_INFO SystemInformation;
    PWORK_QUEUE WorkQueue;

    //
    // Query the number of processors from the system and
    // default the number of worker threads to 4 times that.
    //

    GetSystemInfo( &SystemInformation );
    NumberOfWorkerThreads = SystemInformation.dwNumberOfProcessors * 4;

    //
    // Process the arguments given on the command line.
    //

    if (!ProcessCommandLineArguments( argc, argv )) {
        exit( 1 );
        }

    //
    // Allocate a thread local storage slot for use by our worker
    // thread routine (ProcessRequest).  This call reserves a
    // 32-bit slot in the thread local storage array for every
    // thread in this process.  Remember the slot index in a global
    // variable for use by our worker thread routine.
    //

    TlsIndex = TlsAlloc();
    if (TlsIndex == 0xFFFFFFFF) {
        fprintf( stderr, "PDC: Unable to allocated thread local storage.\n" );
        exit( 1 );
        }

    //
    // Create a work queue, which will create the specified number of threads
    // to process.
    //

    WorkQueue = CreateWorkQueue( NumberOfWorkerThreads, ProcessRequest );
    if (WorkQueue == NULL) {
        fprintf( stderr, "PDC: Unable to create %u worker threads.\n", NumberOfWorkerThreads );
        exit( 1 );
        }

    //
    // If using asynchronous I/O, create an event that will be signalled
    // when there are no more outstanding I/O requests.  The event is
    // a manual reset event, that once signalled via SetEvent, will
    // remain signalled until ResetEvent is called.
    //

    if (ASyncIO) {
        IoCompletedEvent = CreateEvent( NULL,   // Not inherited
                                        TRUE,   // Manual reset
                                        FALSE,  // Initially reset
                                        NULL    // No name
                                      );
        }

    //
    // Now walk the directory tree, which will call our procedure
    // (QueueSearchFile) for each directory and file in the tree.
    //

    EnumerateDirectoryTree( DirectoryPath,
                            QueueSearchFile,
                            WorkQueue
                          );

    //
    // Done walking the tree.  If using asynchronous I/O, wait for all of
    // the outstanding I/O requests to be completed.
    //

    if (ASyncIO) {
        //
        // We use an alertable wait in a loop, as I/O completion
        // will terminate the wait, even through the event we
        // are waiting on is not signalled.
        //

        while (WaitForSingleObjectEx( IoCompletedEvent,
                                      0xFFFFFFFF,
                                      TRUE
                                    ) == WAIT_IO_COMPLETION
              ) {
            ;
            }
        }

    //
    // All done, destroy the work queue.  This will wait for the work queues
    // to empty before terminating the worker threads and destroying the
    // queue.
    //

    DestroyWorkQueue( WorkQueue );

    if (Verbose && MatchedLineCount) {
        fprintf( stderr,
                 "Found %u lines with matches in %u files, out of %u files searched.\n",
                 MatchedLineCount,
                 MatchedFileCount,
                 SearchedFileCount
               );
        }

    return 0;
}

VOID
QueueSearchFile(
    LPSTR Path,
    PWIN32_FIND_DATA FindFileData,
    PVOID EnumerateParameter
    )

/*++

Routine Description:

    This is the directory enumeration function.  It is called by the
    EnumerateDirectoryTree function once for each file and directory
    in the tree.

    This function, if it decides it wants to search the file, will
    open the file and then, depending upon the I/O method selected via
    the command line, will:

        - map the file PAGE_READONLY for mapped file I/O

        - read the file into an allocated buffer for synchronous
          I/O.

        - will allocate the buffer and start a read operation
          to read the entire file into the buffer.  A completion
          routine will be invoked when the read completes, possibly
          in another thread context.

    Finally it will queue a search request to the work queue, with the
    relevant information contained in the request.  For asynchronous
    I/O, the search request is allocated and initialized here but is
    not actually queued to the work queue until the I/O completion
    routine has been called.

Arguments:

    Path - Supplies a pointer to a null terminated string that contains
        the fully qualified path of the file or directory.

    FindFileData - Supplies the directory information associated
        with the file or directory specified by the Path argument.

    EnumerateParameter - Uninterpreted 32-bit value.  Not used.

Return Value:

    None.

--*/

{
    PWORK_QUEUE WorkQueue = (PWORK_QUEUE)EnumerateParameter;
    PSEARCH_REQUEST SearchRequest;
    HANDLE File;
    HANDLE Mapping;
    LPVOID FileData;
    DWORD FileSize;

    //
    // Ignore directories or zero length files, as there
    // is nothing to search in these cases.
    //

    if (FindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ||
        !(FileSize = FindFileData->nFileSizeLow)
       ) {
        return;
        }

    //
    // Open the file using the fully qualified path.  Specify the
    // sequential scan hint to the cache manager and if asynchronous
    // I/O will be used, specified the overlapped flag as well.
    //

    File = CreateFile( Path,
                       GENERIC_READ,
                       FILE_SHARE_READ,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_SEQUENTIAL_SCAN |
                         (ASyncIO ? FILE_FLAG_OVERLAPPED : 0),
                       NULL
                     );

    //
    // Since NULL might be a valid file object handle, failure
    // is indicated by a special return value.
    //

    if (File == INVALID_HANDLE_VALUE) {
        fprintf( stderr, "%s(0) : error %u: Unable to open file.\n",
                 Path,
                 GetLastError()
               );

        return;
        }

    //
    // File successfully opened for read access.
    //
    if (MappedFileIO) {
        //
        // If mapped file I/O, create a file mapping object, backed by
        // the file we just opened.  Make the default page protection
        // for the mapping be readonly.
        //

        Mapping = CreateFileMapping( File,
                                     NULL,
                                     PAGE_READONLY,
                                     0,
                                     0,
                                     NULL
                                   );

        //
        // Okay to close the file handle now, since the mapping object
        // has a reference to the file that will cause the open file
        // object to remain until the reference is destroyed.  Note that
        // any sharing information is lost at this point, so somebody
        // could come in an open it for write access.
        //

        CloseHandle( File );

        //
        // Here, a null value indicates an error.
        //

        if (Mapping == NULL) {
            fprintf( stderr, "%s(0) : error %u: Unable to create mapping object.\n",
                     Path,
                     GetLastError()
                   );
            return;
            }

        //
        // Finally, map a view of the file, using the mapping object
        // just created, into the address space of this process.
        // The offset and size are zero, which means to map the
        // entire file.
        //

        FileData = MapViewOfFile( Mapping,
                                  FILE_MAP_READ,
                                  0,
                                  0,
                                  0
                                );

        //
        // Okay to close the mapping object handle now, it will remained
        // referenced as long as the view remains mapped.
        //

        CloseHandle( Mapping );

        //
        // A null value indicates the map operation failed.
        //

        if (FileData == NULL) {
            fprintf( stderr, "%s(0) : error %u: Unable to map file.\n",
                     Path,
                     GetLastError()
                   );
            return;
            }

        //
        // All done mapping the file.  Set the File handle to NULL as
        // it has been closed already.  Both the file object and mapping
        // objects created above will be freed when the map view is
        // unmapped.
        //

        File = NULL;
        }
    else {
        //
        // Not using mapped I/O, so allocate a buffer big enough to
        // contain the entire file.
        //

        FileData = VirtualAlloc( NULL,
                                 FileSize,
                                 MEM_COMMIT,
                                 PAGE_READWRITE
                               );
        if (FileData == NULL) {
            fprintf( stderr, "%s(0) : error %u: Unable to allocate memory to contain file.\n",
                     Path,
                     GetLastError()
                   );
            CloseHandle( File );
            return;
            }
        }

    //
    // We have successfully opened the file and are ready to go.
    // Allocate space for a search request and fill it in
    // with the information needed by the ProcessSearchFile
    // function when it runs.
    //

    SearchRequest = LocalAlloc( LMEM_ZEROINIT,
                                sizeof( *SearchRequest ) +
                                    strlen( Path ) + 1
                              );
    if (SearchRequest == NULL) {
        fprintf( stderr, "PDC: Out of memory\n" );
        exit( 1 );
        }

    SearchRequest->WorkItem.Reason = WORK_ITEM;
    SearchRequest->File = File;
    SearchRequest->FileSize = FileSize;
    SearchRequest->FileData = FileData;
    strcpy( SearchRequest->FullPathName, Path );

    if (!ASyncIO) {
        //
        // If not using asynchronous I/O, then queue the search
        // request to the work queue.
        //

        QueueWorkItem( WorkQueue, &SearchRequest->WorkItem );
        }
    else {
        //
        // Using asynchronous I/O, so queue the read operation.
        // The file handle must remain open while the read operation
        // is pending.
        //

        if (!ReadFileEx( File,
                         FileData,
                         FileSize,
                         &SearchRequest->OverlappedIO,
                         (LPOVERLAPPED_COMPLETION_ROUTINE) ProcessReadFileCompletion
                       )
           ) {
            fprintf( stderr, "%s(0) : error %u: Unable to queue read of file.\n",
                     Path,
                     GetLastError()
                   );

            VirtualFree( FileData, 0, MEM_RELEASE );
            CloseHandle( File );
            LocalFree( SearchRequest );
            return;
            }

        //
        // Successfully queued the read operation.  Keep a count
        // of outstanding read operations so we know when it is
        // okay to terminate.
        //

        OutstandingIOOperations += 1;
        }

    //
    // Return back to the EnumerateDirectoryTree function so that it
    // can call us with the next file or directrry.
    //

    return;
}

VOID
ProcessRequest(
    IN PWORK_QUEUE_ITEM WorkItem
    )

/*++

Routine Description:

    This function is called whenever a work item is removed from
    the work queue by one of the worker threads.  Which worker
    thread context this function is called in is arbitrary.

    This functions keeps a pointer to state information in
    thread local storage.

    This function is called once at the beginning with a
    special initialization call.  During this call, this
    function allocates space for state information and
    remembers the pointer to the state information in
    a Thread Local Storage (TLS) slot.

    This function is called once at the end with a special
    termination call.  During this call, this function
    frees the state information allocated during the
    initialization call.

    In between these two calls are zero or more calls to
    handle a work item.  The work item is a search request
    which is handled by the ProcessSearchFile function.

Arguments:

    WorkItem - Supplies a pointer to the work item just removed
        from the work queue.  It is the responsibility of this
        routine to free the memory used to hold the work item.

Return Value:

    None.

--*/

{
    DWORD BytesWritten;
    PSEARCH_REQUEST_STATE State;
    PSEARCH_REQUEST SearchRequest;
    CHAR MessageBuffer[ 2 * MAX_PATH ];

    if (WorkItem->Reason == WORK_INITIALIZE_ITEM) {
        //
        // First time initialization call.  Allocate space for
        // state information.
        //

        State = LocalAlloc( LMEM_ZEROINIT,
                            sizeof( *State )
                          );

        if (State != NULL) {
            //
            // Now create a virtual buffer, with an initial commitment
            // of zero and a maximum commitment of 128KB.  This buffer
            // will be used to accumulate the matched strings output
            // during the search of a single file.  This is so the
            // output can be written to standard output with a single
            // write call, thus insuring that it remains contiguous
            // in the output stream, and is not intermingled with the
            // output of the other worker threads.
            //

            if (CreateVirtualBuffer( &State->Buffer, 0, 2 * 64 * 1024 )) {
                //
                // The CurrentOutput field of the state block is
                // a pointer to where the next output goes in the
                // buffer.  It is initialized here and reset each
                // time the buffer is flushed to standard output.
                //

                State->CurrentOutput = State->Buffer.Base;
                }
            else {
                LocalFree( State );
                State = NULL;
                }
            }

        //
        // Remember the pointer to the state informaiton
        // thread local storage.
        //

        TlsSetValue( TlsIndex, State );
        return;
        }

    //
    // Here to handle a work item or special terminate call.
    // Get the state pointer from thread local storage.
    //

    State = (PSEARCH_REQUEST_STATE)TlsGetValue( TlsIndex );
    if (State == NULL) {
        return;
        }

    //
    // If this is the special terminate work item, free the virtual
    // buffer and state block allocated above and set the thread
    // local storage value to NULL.  Return to caller.
    //

    if (WorkItem->Reason == WORK_TERMINATE_ITEM) {
        FreeVirtualBuffer( &State->Buffer );
        LocalFree( State );
        TlsSetValue( TlsIndex, NULL );
        return;
        }

    //
    // If not an initialize or terminate work item, then must be a
    // search request.  Calculate the address of the search request
    // block, based on the position of the WorkItem field in the
    // SEARCH_REQUEST structure.
    //

    SearchRequest = CONTAINING_RECORD( WorkItem, SEARCH_REQUEST, WorkItem );

    //
    // Actual search operation is protected by a try ... except
    // block so that any attempts to store into the virtual buffer
    // will be handled correctly by extending the virtual buffer.
    //

    _try {
        //
        // Perform the search against this file.
        //

        ProcessSearchFile( SearchRequest, State );

        //
        // Done with this file.  If using asynchronous I/O, decrement the
        // count of outstanding I/O operations and it if goes to zero,
        // then signal the IoCompletedEvent as there are no more outstanding
        // I/O operations.
        //

        if (ASyncIO && InterlockedDecrement( &OutstandingIOOperations ) == 0) {
            SetEvent( IoCompletedEvent );
            }

        //
        // If any output was written to the virtual buffer,
        // flush the output to standard output.  Trim the
        // virtual buffer back to zero committed pages.
        //

        if (State->CurrentOutput > (LPSTR)State->Buffer.Base) {
            WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
                       State->Buffer.Base,
                       State->CurrentOutput - (LPSTR)State->Buffer.Base,
                       &BytesWritten,
                       NULL
                     );

            TrimVirtualBuffer( &State->Buffer );
            State->CurrentOutput = (LPSTR)State->Buffer.Base;
            }
        }

    _except( VirtualBufferExceptionFilter( GetExceptionCode(),
                                          GetExceptionInformation(),
                                          &State->Buffer
                                        )
          ) {

        //
        // We will get here if the exception filter was unable to
        // commit the memory.
        //

        WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
                   MessageBuffer,
                   sprintf( MessageBuffer,
                            "%s(0) : error 0: too many matches for file\n",
                            SearchRequest->FullPathName
                          ),
                   &BytesWritten,
                   NULL
                 );
        }

    //
    // Free the storage used by the SearchRequest
    //

    LocalFree( SearchRequest );

    //
    // All done with this request.  Return to the worker thread that
    // called us.
    //

    return;
}

VOID
ProcessReadFileCompletion(
    DWORD dwErrorCode,
    DWORD dwNumberOfBytesTransfered,
    LPOVERLAPPED lpOverlapped
    )

/*++

Routine Description:

    This function is called whenever an asynchronous I/O operation,
    queued by the previous function, completes.  This function
    calulates the address of the search request block and then
    queue the request to the work queue, now that the data is
    in memory.

Arguments:

    dwErrorCode - Supplies the error code that the I/O completed with.

    dwNumberOfBytesTransfered - Supplies the actual number of bytes
        transferred.

    lpOverlapped - Supplies a pointer to the structure given to
        ReadFileEx when the I/O operation was queued.

Return Value:

    None.

--*/

{
    PSEARCH_REQUEST SearchRequest;

    //
    // Since the data we need is now in memory, queue the search
    // request to the work queue.
    //

    SearchRequest = CONTAINING_RECORD( lpOverlapped, SEARCH_REQUEST, OverlappedIO );
    QueueWorkItem( SearchRequest->WorkItem.WorkQueue, &SearchRequest->WorkItem );
}

VOID
ProcessSearchFile(
    IN PSEARCH_REQUEST SearchRequest,
    IN PSEARCH_REQUEST_STATE State
    )

/*++

Routine Description:

    This function performs the actual search of the contents of the
    passed file for the search string given on the command line.
    If we are using synchronous I/O, then do the read operation
    now.

    Search the contents of the file for any matches, and accumulate
    the match output in the virtual buffer using sprintf, which is
    multi-thread safe, even with the single threaded version of
    the libraries.

Arguments:

    SearchRequest - Supplies a pointer to the search request which
        contains the relevant information.

    State - Supplies a pointer to state information for the current
        thread.

Return Value:

    None.

--*/

{
    LPSTR FileData, s, s1, BegLine, EndLine, EndOfFile;
    DWORD LineNumber;
    DWORD MatchesFound;
    DWORD BytesRead;

    // Get a pointer to the beginning of the file data in memory
    // and calculate the address of the end of file point in
    // memory.
    //

    FileData = SearchRequest->FileData;
    EndOfFile = FileData + SearchRequest->FileSize;

    //
    // If using synchronous I/O, then we have not read in the
    // file contents yet, so issue the synchronous read to get
    // the data into memory.
    //

    if (SyncIO) {
        if (!ReadFile( SearchRequest->File,
                       FileData,
                       SearchRequest->FileSize,
                       &BytesRead,
                       NULL
                     ) ||
            BytesRead != SearchRequest->FileSize
           ) {
            State->CurrentOutput += sprintf( State->CurrentOutput,
                                             "%s(0) : error %u: Unable to read file contents.\n",
                                             SearchRequest->FullPathName,
                                             GetLastError()
                                           );

            CloseHandle( SearchRequest->File );
            return;
            }
        }

    //
    // Close any open file handle associated with this request.
    //

    if (SearchRequest->File != NULL) {
        CloseHandle( SearchRequest->File );
        }

    //
    // Search the file contents, keeping track of line breaks
    // so we can tell the line number of each match.
    //

    s = FileData;
    LineNumber = 0;
    MatchesFound = 0;
    while (s < EndOfFile) {
        BegLine = s;
        while (s < EndOfFile && *s != '\n') {
            s++;
            }

        if (*s == '\n') {
            LineNumber += 1;
            EndLine = s - 1;
            if (EndLine > BegLine && EndLine[ -1 ] == '\r') {
                EndLine -= 1;
                }

            s1 = BegLine;
            while (s1 < (EndLine - SearchStringLength)) {
                if (!(SearchFunction)( s1, SearchString, SearchStringLength )) {
                    //
                    // We have a match for this line.  Append the
                    // output to the virtual buffer and update the
                    // current output pointer.
                    //

                    State->CurrentOutput += sprintf( State->CurrentOutput,
                                                     "%s(%u) : %.*s\n",
                                                     SearchRequest->FullPathName,
                                                     LineNumber,
                                                     EndLine - BegLine,
                                                     BegLine
                                                   );
                    MatchesFound += 1;
                    break;
                    }

                s1++;
                }

            s++;
            }
        }

    if (MatchesFound) {
        MatchedLineCount += MatchesFound;
        MatchedFileCount += 1;
        }
    SearchedFileCount += 1;

    //
    // All done with the file contents.  Discard it either by
    // unmapping the view of the file in the case of mapped file
    // I/O or free the virtual memory for other types of I/O
    //

    if (MappedFileIO) {
        if (!UnmapViewOfFile( FileData )) {
            State->CurrentOutput += sprintf( State->CurrentOutput,
                                             "%s(%u) : UnmapViewOfFile( %08x ) failed, error == %u\n",
                                             SearchRequest->FullPathName,
                                             LineNumber,
                                             FileData,
                                             GetLastError()
                                           );
            }
        }
    else {
        VirtualFree( FileData, 0, MEM_RELEASE );
        }
}

PWORK_QUEUE
CreateWorkQueue(
    IN DWORD NumberOfWorkerThreads,
    IN PWORKER_ROUTINE WorkerRoutine
    )

/*++

Routine Description:

    This function creates a work queue, with the specified number of
    threads to service work items placed in the queue.  Work items
    are removed from the queue in the same order that they are placed
    in the queue.

Arguments:

    NumberOfWorkerThreads - Specifies how many threads this function
        should create to process work items placed in the queue.
        Must be greater than 0 and less than 128.

    WorkerRoutine - Specifies the address of a routine to call
        for each work item as it is removed from the queue.  The
        thread context the routine is called in is undefined.

Return Value:

    A pointer to the work queue.  Returns NULL if unable to create
    the work queue and its worker threads.  Extended error information
    is available from GetLastError()

--*/

{
    PWORK_QUEUE WorkQueue;
    HANDLE Thread;
    DWORD ThreadId;
    DWORD i;

    //
    // Allocate space for the work queue, which includes an
    // array of thread handles.
    //

    WorkQueue = LocalAlloc( LMEM_ZEROINIT,
                            sizeof( *WorkQueue ) +
                                (NumberOfWorkerThreads * sizeof( HANDLE ))

);
    if (WorkQueue == NULL) {
        return NULL;
        }

    //
    // The work queue is controlled by a counting semaphore that
    // is incremented each time a work item is placed in the queue
    // and decremented each time a worker thread wakes up to remove
    // an item from the queue.
    //

    if (WorkQueue->Semaphore = CreateSemaphore( NULL, 0, 100000, NULL )) {
        //
        // Mutual exclusion between the worker threads accessing
        // the work queue is done with a critical section.
        //

        InitializeCriticalSection( &WorkQueue->CriticalSection );

        //
        // The queue itself is just a doubly linked list, where
        // items are placed in the queue at the tail of the list
        // and removed from the queue from the head of the list.
        //

        InitializeListHead( &WorkQueue->Queue );

        //
        // Removed the address of the supplied worker function
        // in the work queue structure.
        //

        WorkQueue->WorkerRoutine = WorkerRoutine;

        //
        // Now create the requested number of worker threads.
        // The handle to each thread is remembered in an
        // array of thread handles in the work queue structure.
        //

        for (i=0; i<NumberOfWorkerThreads; i++) {
            Thread = CreateThread( NULL,
                                   0,
                                   (LPTHREAD_START_ROUTINE) WorkerThread,
                                   WorkQueue,
                                   0,
                                   &ThreadId
                                 );
            if (Thread == NULL) {
                break;
                }
            else {
                WorkQueue->NumberOfWorkerThreads++;
                WorkQueue->WorkerThreads[ i ] = Thread;
                SetThreadPriority( Thread, THREAD_PRIORITY_ABOVE_NORMAL );
                }
            }

        //
        // If we successfully created all of the worker threads
        // then return the address of the work queue structure
        // to indicate success.
        //

        if (i == NumberOfWorkerThreads) {
            return WorkQueue;
            }
        }

    //
    // Failed for some reason.  Destroy whatever we managed
    // to create and return failure to the caller.
    //

    DestroyWorkQueue( WorkQueue );
    return NULL;
}

VOID
DestroyWorkQueue(
    IN OUT PWORK_QUEUE WorkQueue
    )

/*++

Routine Description:

    This function destroys a work queue created with the CreateWorkQueue
    functions.  It attempts to shut down the worker threads cleanly
    by queueing a terminate work item to each worker thread.  It then
    waits for all the worker threads to terminate.  If the wait is
    not satisfied within 30 seconds, then it goes ahead and terminates
    all of the worker threads.

Arguments:

    WorkQueue - Supplies a pointer to the work queue to destroy.

Return Value:

    None.

--*/

{
    DWORD i;
    DWORD rc;

    //
    // If the semaphore handle field is not NULL, then there
    // may be threads to terminate.
    //

    if (WorkQueue->Semaphore != NULL) {
        //
        // Set the termiating flag in the work queue and
        // signal the counting semaphore by the number
        // worker threads so they will all wake up and
        // notice the terminating flag and exit.
        //

        EnterCriticalSection( &WorkQueue->CriticalSection );
        _try {
            WorkQueue->Terminating = TRUE;
            ReleaseSemaphore( WorkQueue->Semaphore,
                              WorkQueue->NumberOfWorkerThreads,
                              NULL
                            );
            }
        _finally {
            LeaveCriticalSection( &WorkQueue->CriticalSection );
            }

        //
        // Wait for all worker threads to wake up and see the
        // terminate flag and then terminate themselves.  Timeout
        // the wait after 30 seconds.
        //

        while (TRUE) {
            rc = WaitForMultipleObjectsEx( WorkQueue->NumberOfWorkerThreads,
                                           WorkQueue->WorkerThreads,
                                           TRUE,
                                           30000,
                                           TRUE
                                         );
            if (rc == WAIT_IO_COMPLETION) {
                //
                // If we came out of the wait because an I/O
                // completion routine was called, reissue the
                // wait.
                //
                continue;
                }
            else {
                break;
                }
            }

        //
        // Now close our thread handles so they will actually
        // evaporate.  If the wait above was unsuccessful,
        // then first attempt to force the termination of
        // each worker thread prior to closing the handle.
        //

        for (i=0; i<WorkQueue->NumberOfWorkerThreads; i++) {
            if (rc != NO_ERROR) {
                TerminateThread( WorkQueue->WorkerThreads[ i ], rc );
                }

            CloseHandle( WorkQueue->WorkerThreads[ i ] );
            }

        //
        // All threads stopped, all thread handles closed.  Now
        // delete the critical section and close the semaphore
        // handle.
        //

        DeleteCriticalSection( &WorkQueue->CriticalSection );
        CloseHandle( WorkQueue->Semaphore );
        }

    //
    // Everything done, now free the memory used by the work queue.
    //

    LocalFree( WorkQueue );
    return;
}

BOOL
QueueWorkItem(
    IN OUT PWORK_QUEUE WorkQueue,
    IN PWORK_QUEUE_ITEM WorkItem
    )

/*++

Routine Description:

    This function queues a work item to the passed work queue that is
    processed by one of the worker threads associated with the queue.

Arguments:

    WorkQueue - Supplies a pointer to the work queue that is to
        receive the work item.

    WorkItem - Supplies a pointer to the work item to add the the queue.
        The work item structure contains a doubly linked list entry, the
        address of a routine to call and a parameter to pass to that
        routine.  It is the routine's responsibility to reclaim the
        storage occupied by the WorkItem structure.

Return Value:

    TRUE if operation was successful.  Otherwise returns FALSE and
    extended error information is available from GetLastError()

--*/

{
    BOOL Result;

    //
    // Acquire the work queue critical section and insert the work item
    // in the queue and release the semaphore if the work item is not
    // already in the list.
    //

    EnterCriticalSection( &WorkQueue->CriticalSection );
    Result = TRUE;
    _try {
        WorkItem->WorkQueue = WorkQueue;
        InsertTailList( &WorkQueue->Queue, &WorkItem->List );
        Result = ReleaseSemaphore( WorkQueue->Semaphore, 1, NULL );
        }
    _finally {
        LeaveCriticalSection( &WorkQueue->CriticalSection );
        }

    return Result;
}

DWORD
WorkerThread(
    LPVOID lpThreadParameter
    )
{
    PWORK_QUEUE WorkQueue = (PWORK_QUEUE)lpThreadParameter;
    DWORD rc;
    WORK_QUEUE_ITEM InitWorkItem;
    PWORK_QUEUE_ITEM WorkItem;

    //
    // Call the worker routine with an initialize work item
    // to give it a change to initialize some per thread
    // state that will passed to it for each subsequent
    // work item.
    //

    InitWorkItem.Reason = WORK_INITIALIZE_ITEM;
    (WorkQueue->WorkerRoutine)( &InitWorkItem );
    while( TRUE ) {
        _try {

            //
            // Wait until something is put in the queue (semaphore is
            // released), remove the item from the queue, mark it not
            // inserted, and execute the specified routine.
            //

            rc = WaitForSingleObjectEx( WorkQueue->Semaphore, 0xFFFFFFFF, TRUE );
            if (rc == WAIT_IO_COMPLETION) {
                continue;
                }

            EnterCriticalSection( &WorkQueue->CriticalSection );
            _try {
                if (WorkQueue->Terminating && IsListEmpty( &WorkQueue->Queue )) {
                    break;
                    }

                WorkItem = (PWORK_QUEUE_ITEM)RemoveHeadList( &WorkQueue->Queue );
                }
            _finally {
                LeaveCriticalSection( &WorkQueue->CriticalSection );
                }

            //
            // Execute the worker routine for this work item.
            //

            (WorkQueue->WorkerRoutine)( WorkItem );
            }
        _except( EXCEPTION_EXECUTE_HANDLER ) {
            //
            // Ignore any exceptions from worker routine.
            //
            }
        }

    InitWorkItem.Reason = WORK_TERMINATE_ITEM;
    (WorkQueue->WorkerRoutine)( &InitWorkItem );

    ExitThread( 0 );
    return 0;       // This will exit this thread
}

BOOL
CreateVirtualBuffer(
    OUT PVIRTUAL_BUFFER Buffer,
    IN DWORD CommitSize,
    IN DWORD ReserveSize OPTIONAL
    )

/*++

Routine Description:

    This function is called to create a virtual buffer.  A virtual
    buffer is a contiguous range of virtual memory, where some initial
    prefix portion of the memory is committed and the remainder is only
    reserved virtual address space.  A routine is provided to extend the
    size of the committed region incrementally or to trim the size of
    the committed region back to some specified amount.

Arguments:

    Buffer - Pointer to the virtual buffer control structure that is
        filled in by this function.

    CommitSize - Size of the initial committed portion of the buffer.
        May be zero.

    ReserveSize - Amount of virtual address space to reserve for the
        buffer.  May be zero, in which case amount reserved is the
        committed size plus one, rounded up to the next 64KB boundary.

Return Value:

    TRUE if operation was successful.  Otherwise returns FALSE and
    extended error information is available from GetLastError()

--*/

{
    SYSTEM_INFO SystemInformation;

    //
    // Query the page size from the system for rounding
    // our memory allocations.
    //

    GetSystemInfo( &SystemInformation );
    Buffer->PageSize = SystemInformation.dwPageSize;

    //
    // If the reserve size was not specified, default it by
    // rounding up the initial committed size to a 64KB
    // boundary.  This is because the Win32 Virtual Memory
    // API calls always allocate virtual address space on
    // 64KB boundaries, so we might well have it available
    // for commitment.
    //

    if (!ARGUMENT_PRESENT( ReserveSize )) {
        ReserveSize = ROUND_UP( CommitSize + 1, 0x10000 );
        }

    //
    // Attempt to reserve the address space.
    //

    Buffer->Base = VirtualAlloc( NULL,
                                 ReserveSize,
                                 MEM_RESERVE,
                                 PAGE_READWRITE
                               );
    if (Buffer->Base == NULL) {
        //
        // Unable to reserve address space, return failure.
        //

        return FALSE;
        }

    //
    // Attempt to commit some initial portion of the reserved region.
    //
    //

    CommitSize = ROUND_UP( CommitSize, Buffer->PageSize );
    if (CommitSize == 0 ||
        VirtualAlloc( Buffer->Base,
                      CommitSize,
                      MEM_COMMIT,
                      PAGE_READWRITE
                    ) != NULL
       ) {
        //
        // Either the size of the committed region was zero or the
        // commitment succeeded.  In either case calculate the
        // address of the first byte after the committed region
        // and the address of the first byte after the reserved
        // region and return successs.
        //

        Buffer->CommitLimit = (LPVOID)
            ((char *)Buffer->Base + CommitSize);

        Buffer->ReserveLimit = (LPVOID)
            ((char *)Buffer->Base + ReserveSize);

        return TRUE;
        }

    //
    // If unable to commit the memory, release the virtual address
    // range allocated above and return failure.
    //

    VirtualFree( Buffer->Base, 0, MEM_RELEASE );
    return FALSE;
}

BOOL
ExtendVirtualBuffer(
    IN PVIRTUAL_BUFFER Buffer,
    IN LPVOID Address
    )

/*++

Routine Description:

    This function is called to extend the committed portion of a virtual
    buffer.

Arguments:

    Buffer - Pointer to the virtual buffer control structure.

    Address - Byte at this address is committed, along with all memory
        from the beginning of the buffer to this address.  If the
        address is already within the committed portion of the virtual
        buffer, then this routine does nothing.  If outside the reserved
        portion of the virtual buffer, then this routine returns an
        error.

        Otherwise enough pages are committed so that the memory from the
        base of the buffer to the passed address is a contiguous region
        of committed memory.

Return Value:

    TRUE if operation was successful.  Otherwise returns FALSE and
    extended error information is available from GetLastError()

--*/

{
    DWORD NewCommitSize;
    LPVOID NewCommitLimit;

    //
    // See if address is within the buffer.
    //

    if (Address >= Buffer->Base && Address < Buffer->ReserveLimit) {
        //
        // See if the address is within the committed portion of
        // the buffer.  If so return success immediately.
        //

        if (Address < Buffer->CommitLimit) {
            return TRUE;
            }

        //
        // Address is within the reserved portion.  Determine how many
        // bytes are between the address and the end of the committed
        // portion of the buffer.  Round this size to a multiple of
        // the page size and this is the amount we will attempt to
        // commit.
        //

        NewCommitSize =
            ((DWORD)ROUND_UP( (DWORD)Address + 1, Buffer->PageSize ) -
             (DWORD)Buffer->CommitLimit
            );

        //
        // Attempt to commit the memory.
        //

        NewCommitLimit = VirtualAlloc( Buffer->CommitLimit,
                                       NewCommitSize,
                                       MEM_COMMIT,
                                       PAGE_READWRITE
                                     );
        if (NewCommitLimit != NULL) {
            //
            // Successful, so update the upper limit of the committed
            // region of the buffer and return success.
            //

            Buffer->CommitLimit = (LPVOID)
                ((DWORD)NewCommitLimit + NewCommitSize);

            return TRUE;
            }
        }

    //
    // Address is outside of the buffer, return failure.
    //

    return FALSE;
}

BOOL
TrimVirtualBuffer(
    IN PVIRTUAL_BUFFER Buffer
    )

/*++

Routine Description:

    This function is called to decommit any memory that has been
    committed for this virtual buffer.

Arguments:

    Buffer - Pointer to the virtual buffer control structure.

Return Value:

    TRUE if operation was successful.  Otherwise returns FALSE and
    extended error information is available from GetLastError()

--*/

{
    Buffer->CommitLimit = Buffer->Base;
    return VirtualFree( Buffer->Base, 0, MEM_DECOMMIT );
}

BOOL
FreeVirtualBuffer(
    IN PVIRTUAL_BUFFER Buffer
    )
/*++

Routine Description:

    This function is called to free all the memory that is associated
    with this virtual buffer.

Arguments:

    Buffer - Pointer to the virtual buffer control structure.

Return Value:

    TRUE if operation was successful.  Otherwise returns FALSE and
    extended error information is available from GetLastError()

--*/

{
    //
    // Decommit and release all virtual memory associated with
    // this virtual buffer.
    //

    return VirtualFree( Buffer->Base, 0, MEM_RELEASE );
}

int
VirtualBufferExceptionFilter(
    IN DWORD ExceptionCode,
    IN PEXCEPTION_POINTERS ExceptionInfo,
    IN OUT PVIRTUAL_BUFFER Buffer
    )

/*++

Routine Description:

    This function is an exception filter that handles exceptions that
    referenced uncommitted but reserved memory contained in the passed
    virtual buffer.  It this filter routine is able to commit the
    additional pages needed to allow the memory reference to succeed,
    then it will re-execute the faulting instruction.  If it is unable
    to commit the pages, it will execute the callers exception handler.

    If the exception is not an access violation or is an access
    violation but does not reference memory contained in the reserved
    portion of the virtual buffer, then this filter passes the exception
    on up the exception chain.

Arguments:

    ExceptionCode - Reason for the exception.

    ExceptionInfo - Information about the exception and the context
        that it occurred in.

    Buffer - Points to a virtual buffer control structure that defines
        the reserved memory region that is to be committed whenever an
        attempt is made to access it.

Return Value:

    Exception disposition code that tells the exception dispatcher what
    to do with this exception.  One of three values is returned:

        EXCEPTION_EXECUTE_HANDLER - execute the exception handler
            associated with the exception clause that called this filter
            procedure.

        EXCEPTION_CONTINUE_SEARCH - Continue searching for an exception
            handler to handle this exception.

        EXCEPTION_CONTINUE_EXECUTION - Dismiss this exception and return
            control to the instruction that caused the exception.

--*/

{
    LPVOID FaultingAddress;

    //
    // If this is an access violation touching memory within
    // our reserved buffer, but outside of the committed portion
    // of the buffer, then we are going to take this exception.
    //

    if (ExceptionCode == STATUS_ACCESS_VIOLATION) {
        //
        // Get the virtual address that caused the access violation
        // from the exception record.  Determine if the address
        // references memory within the reserved but uncommitted
        // portion of the virtual buffer.
        //

        FaultingAddress = (LPVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[ 1 ];
        if (FaultingAddress >= Buffer->CommitLimit &&
            FaultingAddress <= Buffer->ReserveLimit
           ) {
            //
            // This is our exception.  Try to extend the buffer
            // to including the faulting address.
            //

            if (ExtendVirtualBuffer( Buffer, FaultingAddress )) {
                //
                // Buffer successfully extended, so re-execute the
                // faulting instruction.
                //

                return EXCEPTION_CONTINUE_EXECUTION;
                }
            else {
                //
                // Unable to extend the buffer.  Stop searching
                // for exception handlers and execute the caller's
                // handler.
                //

                return EXCEPTION_EXECUTE_HANDLER;
                }
            }
        }

    //
    // Not an exception we care about, so pass it up the chain.
    //

    return EXCEPTION_CONTINUE_SEARCH;
}

BOOL
EnumerateDirectoryTree(
    LPSTR DirectoryPath,
    PDIRECTORY_ENUMERATE_ROUTINE EnumerateRoutine,
    PVOID EnumerateParameter
    )

/*++

Routine Description:

    This function walks a directory tree, depth first, calling the
    passed enumeration routine for each directory and file found
    in the tree.  The enumeration routine is passed the full path
    of the file, the directory information associated with the file
    and an enumeration parameter that is uninterpreted by this
    function.

Arguments:

    DirectoryPath - Absolute or relative path to the directory that
        will is the root of the tree to enumerate.

    EnumerateRoutine - Pointer to an enumeration routine to call
        for each file and directory found.

    EnumerateParameter - Uninterpreted 32-bit value that is passed
        to the EnumerationRoutine each time it is called.

Return Value:

    TRUE if operation was successful.  Otherwise returns FALSE and
    extended error information is available from GetLastError()

--*/

{
    BOOL Result;
    VIRTUAL_BUFFER Buffer;
    PENUMERATE_DIRECTORY_STATE State;
    PENUMERATE_DIRECTORY_STACK Stack;
    WIN32_FIND_DATA FindFileData;

    //
    // Create a virtual buffer with an initial committed size of
    // our directory state buffer, and a maximum reserved size of
    // the longest possible full path based on the maximum depth
    // we handle and the maximum length of each path component.
    //

    if (!CreateVirtualBuffer( &Buffer,
                              sizeof( ENUMERATE_DIRECTORY_STATE ),
                              sizeof( ENUMERATE_DIRECTORY_STATE ) +
                                 MAX_DEPTH * MAX_PATH
                            )
       ) {
        return FALSE;
        }

    //
    // This buffer will be used to maintain a stack of directory
    // search handles, as well as accumulate the full path string
    // as we descend the directory tree.
    //

    State = (PENUMERATE_DIRECTORY_STATE)Buffer.Base;
    State->Depth = 0;
    Stack = &State->Stack[ 0 ];

    //
    // Enter a try ... finally block so we can insure that we clean
    // up after ourselves on exit.
    //

    _try {
        //
        // First translate the passed in DirectoryPath into a fully
        // qualified path.  This path will be the initial value in
        // our path buffer.  The initial allocation of the path buffer
        // is big enough for this initial request, so does not need
        // to be guarded by a try ... except clause.
        //

        if (GetFullPathName( DirectoryPath, MAX_PATH, State->Path, &Stack->PathEnd )) {
            //
            // Now enter a try ... except block that will be used to
            // manage the commitment of space in the path buffer as
            // we append subdirectory names and file names to it.
            // Using the virtual buffer allows us to handle full
            // path names up to 16KB in length, with an initial
            // allocation of 4KB.
            //

            _try {
                //
                // Walk the directory tree.  The outer loop is executed
                // once for each directory in the tree.
                //

                while (TRUE) {
startDirectorySearch:
                    //
                    // Find the end of the current path, and make sure
                    // there is a trailing path separator.
                    //

                    Stack->PathEnd = strchr( State->Path, '\0' );
                    if (Stack->PathEnd > State->Path && Stack->PathEnd[ -1 ] != '\\') {
                        *(Stack->PathEnd)++ = '\\';
                        }

                    //
                    // Now append the wild card specification that will
                    // let us enumerate all the entries in this directory.
                    // Call FindFirstFile to find the first entry in the
                    // directory.
                    //

                    strcpy( Stack->PathEnd, "*.*" );
                    Stack->FindHandle = FindFirstFile( State->Path,
                                                       &FindFileData
                                                     );
                    if (Stack->FindHandle != INVALID_HANDLE_VALUE) {
                        //
                        // Entry found.  Now loop through the entire
                        // directory processing each entry found,
                        // including the first one.
                        //
                        do {
                            //
                            // Ignore bogus pseudo-directories that are
                            // returned by some file systems (e.g. FAT).
                            //

                            if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
                                (!strcmp( FindFileData.cFileName, "." ) ||
                                 !strcmp( FindFileData.cFileName, ".." )
                                )
                               ) {
                                continue;
                                }

                            //
                            // Copy the file name portion from the current
                            // directory entry to the last component in the
                            // path buffer.
                            //

                            strcpy( Stack->PathEnd, FindFileData.cFileName );

                            //
                            // Call the supplied enumeration routine with the
                            // full path we have built up in the path buffer,
                            // the directory information for this directory
                            // entry and the supplied enumeration parameter.
                            //

                            (*EnumerateRoutine)( State->Path, &FindFileData, EnumerateParameter );

                            //
                            // If this is entry is a subdirectory, then it is
                            // time to recurse.  Do this by incrementing the
                            // stack pointer and depth and jumping to the top
                            // of the outer loop to process current contents
                            // of the path buffer as a fully qualified name of
                            // a directory.
                            //

                            if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                                Stack++;
                                State->Depth++;
                                goto startDirectorySearch;
restartDirectorySearch:         ;
                                }

                            //
                            // Here to find the next entry in the current directory.
                            //
                            }

                        while ( FindNextFile( Stack->FindHandle, &FindFileData ) );

                        //
                        // No more entries in the current directory, so close
                        // the search handle and fall into the code that will
                        // pop our stack of directory seacrh handles.

                        FindClose( Stack->FindHandle );
                        }

                    //
                    // Here when done with a directory.  See if we are pushed
                    // inside another directory.  If not, then we are done
                    // enumerating the whole tree, so break out of the loop.
                    //

                    if (!State->Depth) {
                        Result = TRUE;
                        break;
                        }

                    //
                    // We were pushed within another directory search,
                    // so pop the stack to restore its search handle
                    // and path buffer position and resume the search
                    // within that directory.

                    State->Depth--;
                    --Stack;
                    goto restartDirectorySearch;
                    }
                }

            //
            // Any of the code that appends to the path buffer within
            // the above try ... except clause can cause an access
            // violation if the path buffer becomes longer than its
            // current committed size.  This exception filter
            // will dynamically commit additional pages as needed
            // and resume execution.
            //

            _except( VirtualBufferExceptionFilter( GetExceptionCode(),
                                                  GetExceptionInformation(),
                                                  &Buffer

)
                  ) {
                //
                // We will get here if the exception filter was unable to
                // commit the memory.
                //

                Result = FALSE;
                }
            }
        else {
            //
            // Initial GetFullPathName failed, so return a failure.
            //

            Result = FALSE;
            }
        }
    _finally {
        //
        // Here on our way out of the outer try ... finally block.
        // Make sure all our search handles have been closed and then
        // free the virtual buffer.  The only way this code is not
        // executed is if code within the try ... finally block
        // called ExitThread or ExitProcess, or an external thread
        // or process terminated this thread or process.
        //
        // In the case of process death, this is not a problem, because
        // process terminate closes all open handles attached to the process
        // and frees all private virtual memory that is part of the address
        // space of the process.
        //
        // In the case ot thread death, the code below is not executed if
        // the thread terminates via ExitThread in the context of the
        // try .. finally or if an external thread, either in this process
        // or another process called TerminateThread on this thread.
        //

        while (State->Depth--) {
            --Stack;
            FindClose( Stack->FindHandle );
            }

        FreeVirtualBuffer( &Buffer );
        }

    return Result;
}

BOOL
ProcessCommandLineArguments(
    int argc,
    char *argv[]
    )
{
    BOOL Result;
    LPSTR s;

    Result = FALSE;
    _try {
        if (argc < 1) {
            return Result;
            }

        while (--argc) {
            s = *++argv;
            if (*s == '-' || *s == '/') {
                while (*++s) {
                    switch( tolower( *s ) ) {
                        case 'm':
                            MappedFileIO = TRUE;
                            break;

                        case 'a':
                            ASyncIO = TRUE;
                            break;

                        case 's':
                            SyncIO = TRUE;
                            break;

                        case 'v':
                            Verbose = TRUE;
                            break;

                        case 'y':
                            IgnoreCase = TRUE;
                            break;

                        case 't':
                            if (--argc) {
                                NumberOfWorkerThreads = atoi( *++argv );
                                if (NumberOfWorkerThreads > 0 && NumberOfWorkerThreads < 128) {
                                    break;
                                    }
                                }

                            // fall through if -t argument missing.

                        case '?':
                        case 'h':
                        default:
                            return Result;
                        }
                    }
                }
            else
            if (SearchString == NULL) {
                SearchString = s;
                }
            else
            if (DirectoryPath == NULL) {
                DirectoryPath = s;
                }
            else {
                return Result;
                }
            }

        if (SearchString == NULL) {
            return Result;
            }

        SearchStringLength = strlen( SearchString );
        if (SearchStringLength == 0) {
            return Result;
            }

        if (IgnoreCase) {
            SearchFunction = _strnicmp;
            }
        else {
            SearchFunction = strncmp;
            }

        if (DirectoryPath == NULL) {
            DirectoryPath = ".";
            }

        if (!(MappedFileIO || ASyncIO || SyncIO)) {
            MappedFileIO = TRUE;
            }

        if (Verbose) {
            fprintf( stderr, "Directory Tree: %s\n", DirectoryPath );
            fprintf( stderr, "Search String: '%s'\n", SearchString );
            fprintf( stderr, "Case %ssensitive\n", IgnoreCase ? "in" : "" );
            fprintf( stderr, "Number of Worker Threads: %u\n", NumberOfWorkerThreads );
            if (MappedFileIO) {
                fprintf( stderr, "Using Mapped File I/O\n" );
                }
            else
            if (ASyncIO) {
                fprintf( stderr, "Using ASynchronous File I/O\n" );
                }
            else
            if (MappedFileIO) {
                fprintf( stderr, "Using Synchronous File I/O\n" );
                }
            }

        Result = TRUE;
        return Result;
        }
    _finally {
        if (!Result) {
            fprintf( stderr, "usage: PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]\n" );
            fprintf( stderr, "Where...\n" );
            fprintf( stderr, "    -h - prints this message\n" );
            fprintf( stderr, "    -v - generates verbose output\n" );
            fprintf( stderr, "    -y - ignores case when doing comparision\n" );
            fprintf( stderr, "    -t - specifies the number of threads to use (defaults to 4 * number of processors)\n" );
            fprintf( stderr, "    -a - uses asynchronous file I/O\n" );
            fprintf( stderr, "    -s - uses synchronous file I/O\n" );
            fprintf( stderr, "    -m - uses mapped file I/O (default)\n" );
            fprintf( stderr, "    SearchString - specifies the text to search for\n" );
            fprintf( stderr, "    DirectoryPath - specifies the directory to start from (defaults to .)\n" );
            }
        }
}


PDC.H

/*++

Copyright 1992 - 1998 Microsoft Corporation

Module Name:

    pdc.h

Abstract:

    This is the main source file for the Windows/NT PDC API demonstration
    program.  This include file defines macros, data structures and procedure
    prototypes for the code contained in the PDC.C source file.

--*/

//
// Include file for using the Win32 API
//

#include <windows.h>

//
// C Runtime include files used by the program.
//

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <malloc.h>
#include <sys\types.h>
#include <sys\stat.h>

//
// Macro definitions
//

//
// Useful rounding macros that the rounding amount is always a
// power of two.
//

#define ROUND_DOWN( Size, Amount ) ((DWORD)(Size) & ~((Amount) - 1))
#define ROUND_UP( Size, Amount ) (((DWORD)(Size) + ((Amount) - 1)) & ~((Amount) - 1))

//
// Pseudo keywords for documentation purposes.
//

#define IN
#define OUT
#define OPTIONAL

//
// Determine if an argument is present by testing a value of NULL
//

#define ARGUMENT_PRESENT( ArgumentPointer )    (\
    (LPSTR)(ArgumentPointer) != (LPSTR)(NULL) )

//
// Calculate the address of the base of the structure given its type, and an
// address of a field within the structure.
//

#define CONTAINING_RECORD( address, type, field ) ((type *)( \
                                                   (PCHAR)(address) - \
                                                   (PCHAR)(&((type *)0)->field)))

//
// Virtual Buffer data structure and function prototypes.
//

typedef struct _VIRTUAL_BUFFER {
    LPVOID Base;
    ULONG PageSize;
    LPVOID CommitLimit;
    LPVOID ReserveLimit;
} VIRTUAL_BUFFER, *PVIRTUAL_BUFFER;

BOOL
CreateVirtualBuffer(
    OUT PVIRTUAL_BUFFER Buffer,
    IN DWORD CommitSize,
    IN DWORD ReserveSize OPTIONAL
    );

BOOL
ExtendVirtualBuffer(
    IN PVIRTUAL_BUFFER Buffer,
    IN LPVOID Address
    );

BOOL
TrimVirtualBuffer(
    IN PVIRTUAL_BUFFER Buffer
    );

BOOL
FreeVirtualBuffer(
    IN PVIRTUAL_BUFFER Buffer
    );

int
VirtualBufferExceptionFilter(
    IN DWORD ExceptionCode,
    IN PEXCEPTION_POINTERS ExceptionInfo,
    IN OUT PVIRTUAL_BUFFER Buffer
    );

//
// Directory enumeration and file notification definitions.
//

typedef
VOID (*PDIRECTORY_ENUMERATE_ROUTINE)(
    LPSTR Path,
    PWIN32_FIND_DATA FindFileData,
    PVOID EnumerateParameter
    );

BOOL
EnumerateDirectoryTree(
    LPSTR DirectoryPath,
    PDIRECTORY_ENUMERATE_ROUTINE EnumerateRoutine,
    PVOID EnumerateParameter
    );

//
// Data structures private to the EnumerateDirectoryTree function.
//

typedef struct _ENUMERATE_DIRECTORY_STACK {
    LPSTR PathEnd;
    HANDLE FindHandle;
} ENUMERATE_DIRECTORY_STACK, *PENUMERATE_DIRECTORY_STACK;

#define MAX_DEPTH 256

typedef struct _ENUMERATE_DIRECTORY_STATE {
    DWORD Depth;
    ENUMERATE_DIRECTORY_STACK Stack[ MAX_DEPTH ];
    CHAR Path[ MAX_PATH ];
} ENUMERATE_DIRECTORY_STATE, *PENUMERATE_DIRECTORY_STATE;

//
//  Doubly-linked list manipulation routines.  Implemented as macros
//  but logically these are procedures.
//

//
//  VOID
//  InitializeListHead(
//      PLIST_ENTRY ListHead
//      );
//

#define InitializeListHead(ListHead) (\
    (ListHead)->Flink = (ListHead)->Blink = (ListHead) )

//
//  BOOLEAN
//  IsListEmpty(
//      PLIST_ENTRY ListHead
//      );
//

#define IsListEmpty(ListHead) (\
    ( ((ListHead)->Flink == (ListHead)) ? TRUE : FALSE ) )

//
//  PLIST_ENTRY
//  RemoveHeadList(
//      PLIST_ENTRY ListHead
//      );
//

#define RemoveHeadList(ListHead) \
    (ListHead)->Flink;\
    {\
        PLIST_ENTRY FirstEntry;\
        FirstEntry = (ListHead)->Flink;\
        FirstEntry->Flink->Blink = (ListHead);\
        (ListHead)->Flink = FirstEntry->Flink;\
    }

//
//  VOID
//  RemoveEntryList(
//      PLIST_ENTRY Entry
//      );
//

#define RemoveEntryList(Entry) {\
        PLIST_ENTRY _EX_Entry;\
        _EX_Entry = (Entry);\
        _EX_Entry->Blink->Flink = _EX_Entry->Flink;\
        _EX_Entry->Flink->Blink = _EX_Entry->Blink;\
        }

//
//  VOID
//  InsertTailList(
//      PLIST_ENTRY ListHead,
//      PLIST_ENTRY Entry
//      );
//

#define InsertTailList(ListHead,Entry) \
    (Entry)->Flink = (ListHead);\
    (Entry)->Blink = (ListHead)->Blink;\
    (ListHead)->Blink->Flink = (Entry);\
    (ListHead)->Blink = (Entry)

//
//  VOID
//  InsertHeadList(
//      PLIST_ENTRY ListHead,
//      PLIST_ENTRY Entry
//      );
//

#define InsertHeadList(ListHead,Entry) \
    (Entry)->Flink = (ListHead)->Flink;\
    (Entry)->Blink = (ListHead);\
    (ListHead)->Flink->Blink = (Entry);\
    (ListHead)->Flink = (Entry)

//
// Worker thread definitions
//

typedef
VOID
(*PWORKER_ROUTINE)(
  //  IN PWORK_QUEUE_ITEM WorkItem
    );

typedef struct _WORK_QUEUE {
    CRITICAL_SECTION CriticalSection;
    HANDLE Semaphore;
    LIST_ENTRY Queue;
    BOOL Terminating;
    PWORKER_ROUTINE WorkerRoutine;
    DWORD NumberOfWorkerThreads;
    HANDLE WorkerThreads[ 1 ];      // Variable length array
} WORK_QUEUE, *PWORK_QUEUE;

typedef struct _WORK_QUEUE_ITEM {
    LIST_ENTRY List;
    DWORD Reason;
    PWORK_QUEUE WorkQueue;
} WORK_QUEUE_ITEM, *PWORK_QUEUE_ITEM;

#define WORK_INITIALIZE_ITEM 0
#define WORK_ITEM            1
#define WORK_TERMINATE_ITEM  2

DWORD
WorkerThread(
    LPVOID lpThreadParameter
    );

PWORK_QUEUE
CreateWorkQueue(
    IN DWORD NumberOfWorkerThreads,
    IN PWORKER_ROUTINE WorkerRoutine
    );

BOOL
QueueWorkItem(
    IN OUT PWORK_QUEUE WorkQueue,
    IN PWORK_QUEUE_ITEM WorkItem
    );

VOID
DestroyWorkQueue(
    IN OUT PWORK_QUEUE WorkQueue
    );

//
// Main procedure prototypes
//

BOOL
ProcessCommandLineArguments(
    int argc,
    char *argv[]
    );

VOID
QueueSearchFile(
    LPSTR Path,
    PWIN32_FIND_DATA FindFileData,
    PVOID EnumerateParameter
    );

typedef struct _SEARCH_REQUEST_STATE {
    VIRTUAL_BUFFER Buffer;
    LPSTR CurrentOutput;
} SEARCH_REQUEST_STATE, *PSEARCH_REQUEST_STATE;

typedef struct _SEARCH_REQUEST {
    WORK_QUEUE_ITEM WorkItem;
    DWORD FileSize;
    LPSTR FileData;
    HANDLE File;
    OVERLAPPED OverlappedIO;
    CHAR FullPathName[ 1 ];         // Variable length array
} SEARCH_REQUEST, *PSEARCH_REQUEST;

VOID
ProcessRequest(
    IN PWORK_QUEUE_ITEM WorkItem
    );

VOID
ProcessReadFileCompletion(
    DWORD dwErrorCode,
    DWORD dwNumberOfBytesTransfered,
    LPOVERLAPPED lpOverlapped
    );

VOID
ProcessSearchFile(
    IN PSEARCH_REQUEST WorkerRequest,
    IN PSEARCH_REQUEST_STATE State
    );

//
// Global variables
//

DWORD TlsIndex;
INT NumberOfWorkerThreads;
BOOL Verbose;
BOOL IgnoreCase;
BOOL MappedFileIO;
BOOL ASyncIO;
BOOL SyncIO;
LPSTR DirectoryPath;
LPSTR SearchString;
DWORD SearchStringLength;
DWORD SearchedFileCount;
DWORD MatchedFileCount;
DWORD MatchedLineCount;
LONG  OutstandingIOOperations;
HANDLE IoCompletedEvent;

typedef
int
(_CRTAPI1 *PCOMPARE_ROUTINE)(
    IN const char *,
    IN const char *,
    IN size_t
    );

PCOMPARE_ROUTINE SearchFunction;


README.TXT

File I/O API Demonstration

SUMMARY
=======

The FILER sample is a basic file management applet, such as File Manager or
Norton Commander. It demonstrates many of the new File I/O API, and their
related algorithms, such as drive enumeration, .EXE version information
retrieval, and directory tree walking. It also demonstrates many intensive
User algorithms, such as child management, subclassing, synchronization and
control window management.

MORE INFORMATION
================

FILER presents the user with two fixed child windows, each of which the user
may associate with a drive from the drives available on the system. The
Drive toolbar describes the available drives on the system. Users select
drives from the toolbar or the Drives menu. The Function toolbar corresponds
to the file I/O options in the File menu.

Each of the Drive child windows contains a Directory ListBox, and a File
ListBox, for browsing through the files on the selected drive.

FILER gives the user the option to open (execute or edit), copy, delete,
move, and rename files, create and remove directories, and display any
version information embedded in the files. The active Drive child acts as
the source, and the inactive Drive child acts as the default destination
of file I/O operations.

The Drive children may also be configured side by side or with one above the
other, and the File and Directory ListBoxes in each may swap positions.

All features of FILER may be selected from the mouse, keyboard, or menu
items.


DRVPROC.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/******************************Module*Header*******************************\
* Module Name:  drvproc.c
*
* Filer : SDK sample
*   +   Simple File Management program with GUI front end.
*       Demonstrates Win32 File I/O API and various User algorithms.
*
* DRVPROC.C : Contains procedures relating to child window management.
*               In this sample, the Drive children handle the directory
*               and file enumeration, as well as the file I/O operations.
*
\**************************************************************************/

#define  STRICT
#include <windows.h>
#include <string.h>
#define _MBCS
#include <mbstring.h>
#include "globals.h"
#include "filer.h"
#include "expdir.h"
#include "drvproc.h"

extern HANDLE   ghModule;
extern HANDLE   ghHeap;
extern HFONT    ghFont;
extern HANDLE   ghDrvThread;
extern HANDLE   ghMenu;

extern HWND     ghwndCommand;
extern HWND     ghwndDrives;
extern HWND     ghActiveChild;
extern HWND     ghwndDrv1;
extern HWND     ghwndDrv2;
extern HWND     ghFocusWnd;

extern LPDINFO  glpDrives;

extern CRITICAL_SECTION    gHeapCS;  // Global heap critical section var.
extern CRITICAL_SECTION    gDrvCS;   // Drive list critical section var.

extern TCHAR    gszEditor[DIRECTORY_STRING_SIZE];
extern TCHAR    gszCommandLine[DIRECTORY_STRING_SIZE * 2];

extern TCHAR    gszExtensions[NUM_EXTENSION_STRINGS][EXTENSION_LENGTH];

extern VKINFO   gVKArray[NUM_VERSION_INFO_KEYS];  // .EXE version info array.

/***************************************************************************\
* DrvWndProc()
*
* History:
* 05-1-92  Created
\***************************************************************************/

LRESULT  WINAPI DrvWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    TCHAR lpBuffer[128];
DWORD dwDirStyle = WS_BORDER | WS_CHILD | WS_VISIBLE |
                              LBS_NOINTEGRALHEIGHT | LBS_NOTIFY |
                              LBS_HASSTRINGS | LBS_WANTKEYBOARDINPUT |
                              LBS_DISABLENOSCROLL | WS_HSCROLL |
                              WS_VSCROLL |LBS_USETABSTOPS;

    DWORD dwFileStyle = WS_BORDER | WS_CHILD | WS_VISIBLE |
                               LBS_NOINTEGRALHEIGHT | LBS_NOTIFY |
                               LBS_HASSTRINGS | LBS_WANTKEYBOARDINPUT |
                               LBS_DISABLENOSCROLL | WS_HSCROLL |
                               LBS_EXTENDEDSEL | LBS_MULTIPLESEL |
                               LBS_MULTICOLUMN | LBS_SORT;

    switch (message){

        //
        // Creates the text and listbox windows for this Drv child and
        //  saves its handle in the per Drv child DRVCHILDINFO data structure.
        //
        case WM_CREATE: {
            LPCINFO lpCInfo;

            DWORD   dwLoop;

            LPDINFO lpWalk;

            LONG    lTabs = LISTBOX_TAB_SIZE;

            //
            // Initialize DRVCHILDINFO structure
            //
            lpCInfo = (LPCINFO) ((LPCREATESTRUCT) lParam)->lpCreateParams;

            lpCInfo->hwnd = hwnd;

            // Create text window
            lpCInfo->hTextWnd = CreateWindow(TEXT("TextClass"), NULL,
                                    SS_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER,
                                    0, 0, 0, 0,
                                    lpCInfo->hwnd,
                                    (HMENU) TEXT_WINDOW_ID,
                                    ghModule,
                                    NULL);

            // Create Directory and File List boxes
            lpCInfo->hDirLB = CreateWindow(TEXT("LISTBOX"), NULL,
                                    dwDirStyle,
                                    0, 0, 0, 0,
                                    lpCInfo->hwnd,
                                    (HMENU) LISTDIR_ID,
                                    ghModule,
                                    NULL);

            lpCInfo->hFileLB = CreateWindow(TEXT("LISTBOX"), NULL,
                                    dwFileStyle,
                                    0, 0, 0, 0,
                                    lpCInfo->hwnd,
                                    (HMENU) LISTFILE_ID,
                                    ghModule,
                                    NULL);

            //
            // fDirLeft indicates whether the Directory ListBox defaults to
            //  the left side of each of the two drive windows.
            // fDirExpand indicates whether the Directory Listbox defaults
            //  to full expansion.
            //
            lpCInfo->fDirLeft = TRUE;
            lpCInfo->fDirExpand = FALSE;
            lpCInfo->fSuicide = FALSE;

            //
            // Create Mutex associated with each list box
            //
            lpCInfo->hDirMutex = CreateMutex(NULL, FALSE, NULL);
            lpCInfo->hFileMutex = CreateMutex(NULL, FALSE, NULL);

            //
            // Associate window with the current directory LPDINFO structure
            //   from the Drives linked list
            //
            dwLoop = GetCurrentDirectory( DIRECTORY_STRING_SIZE,
                                          lpCInfo->CaptionBarText );
            CharUpper(lpCInfo->CaptionBarText);

            WaitForSingleObject(ghDrvThread, INFINITE);
            EnterCriticalSection(&gDrvCS);

            lpWalk = glpDrives;

            if( dwLoop && dwLoop <= DIRECTORY_STRING_SIZE ){
                while( lpWalk && lpWalk->DriveName[0] !=
                       (lpCInfo->CaptionBarText)[0] )
                    lpWalk = lpWalk->next;
                if( !lpWalk ){
LoadString(ghModule, IDS_DCCDRVLST, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    LeaveCriticalSection(&gDrvCS);
                    return(-1);
                }
            }
            else{
LoadString(ghModule, IDS_DCCCURDIR, lpBuffer, sizeof(lpBuffer));
                ErrorMsg(lpBuffer);
                LeaveCriticalSection(&gDrvCS);
                return(-1);
            }

            LeaveCriticalSection(&gDrvCS);

            lpCInfo->lpDriveInfo = lpWalk;

            //
            // Save the handle to DRVCHILDINFO in our window structure
            //
            SetWindowLong(hwnd, GWL_USERDATA, (LONG) lpCInfo);

            //
            // Initialize child windows
            //
            if( !SendMessage(lpCInfo->hDirLB, LB_SETTABSTOPS, (WPARAM)1,
                            (LPARAM)&lTabs) ) {
LoadString(ghModule, IDS_DCCTABSTP, lpBuffer, sizeof(lpBuffer));
                ErrorMsg(lpBuffer);
            }

            //
            // Set default font.
            //
            SendMessage(lpCInfo->hDirLB, WM_SETFONT, (WPARAM)ghFont, (LPARAM)FALSE);
            SendMessage(lpCInfo->hFileLB, WM_SETFONT, (WPARAM)ghFont, (LPARAM)FALSE);

            SendMessage(hwnd, WM_COMMAND, (WPARAM)MM_REFRESH, (LPARAM)NULL);

            return(1);
        }

        case WM_COMMAND: {
          static LPCINFO     lpCInfo;
          static SELECTINFO  Select;

          //
          // Retrieving this child window's DRVCHILDINFO data for displaying
          //    messages in the text window
          //
          lpCInfo = (LPCINFO) GetWindowLong(hwnd, GWL_USERDATA);

          switch (LOWORD(wParam)){

            case MM_TAB:{
                HWND    hFocus;

                hFocus = GetFocus();

                if( hFocus == lpCInfo->hDirLB )
                    ghFocusWnd = lpCInfo->hFileLB;
                else
                    if( hFocus == lpCInfo->hFileLB )
                        ghFocusWnd = ghwndCommand;
                    else
                        if( hFocus == ghwndCommand)
                            ghFocusWnd = lpCInfo->hDirLB;

                SetFocus( ghFocusWnd );
                return(1);
            }

            //
            //  Clears the selection in the active window.
            //  Sent when user hits escape key.
            //
            case MM_ESCAPE:{
                //
                // If there is a directory expand in process, kill the
                //  thread, and leave the listbox in a semi-expanded state.
                //  Else, clear file selection, and switch to command window.
                //
                if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
                        == WAIT_TIMEOUT ){
                    lpCInfo->fSuicide = TRUE;
                    lpCInfo->fEscape = TRUE;
                }
                else
                    ReleaseMutex( lpCInfo->hDirMutex );

                SendMessage(lpCInfo->hFileLB, LB_SETCURSEL, (WPARAM)-1,
                               (LPARAM)0);

                SetFocus( ghwndCommand );
                ghFocusWnd = ghwndCommand;
                SendMessage(ghwndCommand, LB_SETCURSEL, (WPARAM)0,
                               (LPARAM)-1);
                return(1);
            }

            case MM_OPEN:{
                if( ghFocusWnd == lpCInfo->hFileLB )
                    OpenListBoxItem(lpCInfo);
                else
                    if( ghFocusWnd == lpCInfo->hDirLB ){
                        if( !PostMessage(hwnd, WM_COMMAND, MM_FILLDIR,
                                         (LPARAM)0) ){
            LoadString(ghModule, IDS_MMOPENERR, lpBuffer, sizeof(lpBuffer));
                            ErrorMsg(lpBuffer);
                            return(0);
                        }
                    }
                    else
                        RunCommandItem(lpCInfo);

                return(1);
            }

            case MM_COPY:{

                Select.hwnd = hwnd;
                Select.dwAction = MM_COPY;
                LoadString(ghModule, IDS_COPYING, lpBuffer, sizeof(lpBuffer));
                Select.szAction = lpBuffer;
                LoadString(ghModule, IDS_COPYINGTO, lpBuffer, sizeof(lpBuffer));
                Select.szToFrom = lpBuffer;

                ExecuteFileAction(&Select);

                return(1);
            }

            case MM_DELETE:{

                Select.hwnd = hwnd;
                Select.dwAction = MM_DELETE;
                LoadString(ghModule, IDS_DELETING, lpBuffer, sizeof(lpBuffer));
                Select.szAction = lpBuffer;
                LoadString(ghModule, IDS_DELETINGFRM, lpBuffer, sizeof(lpBuffer));
                Select.szToFrom = lpBuffer;

                ExecuteFileAction(&Select);

                return(1);
            }

            case MM_MOVE:{

                Select.hwnd = hwnd;
                Select.dwAction = MM_MOVE;
                LoadString(ghModule, IDS_MOVING, lpBuffer, sizeof(lpBuffer));
                Select.szAction = lpBuffer;
                LoadString(ghModule, IDS_COPYINGTO, lpBuffer, sizeof(lpBuffer));
                Select.szToFrom = lpBuffer;

                ExecuteFileAction(&Select);

                return(1);
            }

            case MM_RENAME:{

                if( DialogBoxParam(ghModule, TEXT("RenameDlg"), hwnd,
                              (DLGPROC)RenameProc, (LPARAM)lpCInfo) == -1 ){
                    LoadString(ghModule, IDS_RENAMEERR, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    return(0);
                }

                return(1);
            }

            case MM_MKDIR:{

                if( DialogBoxParam(ghModule, TEXT("MkDirDlg"), hwnd,
                              (DLGPROC)MkDirProc, (LPARAM)lpCInfo) == -1 ){
                    LoadString(ghModule, IDS_MKDIRERR, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    return(0);
                }

                return(1);
            }

            case MM_VERSION:{

                if( DialogBoxParam(ghModule, TEXT("VersionInfoDlg"), hwnd,
                              (DLGPROC)VersionProc, (LPARAM)lpCInfo) == -1 ){
                    LoadString(ghModule, IDS_VERDLGERR, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    return(0);
                }

                return(1);
            }

            case MM_EXPAND:{

                lpCInfo->fDirExpand = !lpCInfo->fDirExpand;

                if( lpCInfo->fDirExpand )
                    CheckMenuItem( ghMenu, MM_EXPAND,
                                    MF_BYCOMMAND | MF_CHECKED);
                else
                    CheckMenuItem( ghMenu, MM_EXPAND,
                                MF_BYCOMMAND | MF_UNCHECKED);

                if( !SendMessage( (HWND)lpCInfo->hwnd, WM_COMMAND,
                                 (WPARAM)MM_REFRESH, (LPARAM)0 ) ){
                    LoadString(ghModule, IDS_MMEXPAND, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    return(0);
                }
                return(1);
            }

            //
            // refreshes contents of directory and file ListBoxes.
            //
            case MM_REFRESH:{

                DWORD   dwThreadID;

                if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
                        == WAIT_TIMEOUT )
                    //
                    // If the full directory expand has been cancled, kill the
                    //  existing thread.
                    //
                    if( !lpCInfo->fDirExpand && !lpCInfo->fSuicide){
                        lpCInfo->fSuicide = TRUE;
                        return(1);
                    }
                    else{
                        return(0);
                    }

                // if set, clear the expand dir. user abort (escape key) flag.
                if( lpCInfo->fEscape ){
                    lpCInfo->fEscape = FALSE;
                    ReleaseMutex( lpCInfo->hDirMutex );
                    return(1);
                }

                // At this point, the Dir LB mutex has been grabbed.

                // Clear directory LB.  If expand flag is set, expand all
                //  directories.  Refill File LB.
                //
                if( SendMessage( lpCInfo->hDirLB, LB_RESETCONTENT,
                                 (WPARAM)0, (LPARAM)0 ) < 0 ){
                    LoadString(ghModule, IDS_REFRESH1, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    ReleaseMutex( lpCInfo->hDirMutex );
                    return(0);
                }

                //
                // This call puts the default root entry back into the empty
                //  LB.  Set suicide flag to false to ensure it will complete.
                //
                lpCInfo->fSuicide = FALSE;
                ExpDir( lpCInfo );

                //
                // All the Dir LB work is done.  Release Dir LB Mutex.
                //
                ReleaseMutex( lpCInfo->hDirMutex );

                if( lpCInfo->fDirExpand ){

                    CloseHandle( lpCInfo->hDirThread );

                    lpCInfo->hDirThread = CreateThread( NULL, 0,
                                  (LPTHREAD_START_ROUTINE)FullExpand,
                                  (LPVOID)lpCInfo, 0, &dwThreadID);

                    if( !lpCInfo->hDirThread ){
                        LoadString(ghModule, IDS_REFRESH2, lpBuffer, sizeof(lpBuffer));
                        ErrorMsg(lpBuffer);
                        return(0);
                    }
                }
                else
                    ExpDir( lpCInfo );

                if( !PostMessage(hwnd, WM_COMMAND, MM_FILLFILE,
                                 (LPARAM)0) ){
                    LoadString(ghModule, IDS_REFRESH3, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    return(0);
                }

                return(1);
            }

            //
            //  Fill listbox in lParam with directory from Drv child's drive.
            //  Sent by MM_REFRESH.
            //
            //  lParam == 0
            //
            case MM_FILLDIR:{

                DWORD   dwThreadID;

                lpCInfo->fSuicide = FALSE;

                CloseHandle( lpCInfo->hDirThread );

                lpCInfo->hDirThread = CreateThread( NULL, 0,
                              (LPTHREAD_START_ROUTINE)ExpDir,
                              (LPVOID)lpCInfo, 0, &dwThreadID);

                if( !(lpCInfo->hDirThread) ){
                    LoadString(ghModule, IDS_FILDIRERR, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    return(0);
                }

                return(1);
            }

            //
            //  Fill listbox in lParam with files from current directory.
            //  Sent by MM_REFRESH & LBN_DBLCLK in DrvWndProc, as well as
            //  DoFileIO. and HandleIOError().
            //
            //  lParam == 0
            //
            case MM_FILLFILE:{
                TCHAR               szFiles[DIRECTORY_STRING_SIZE + 20];
                LPTSTR              lpHold;

                if( WaitForSingleObject( lpCInfo->hFileMutex, MUTEX_TIMEOUT)
                        == WAIT_TIMEOUT ){
                    LoadString(ghModule, IDS_FILDIRERR2, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    return(0);
                }

                //
                // Not checking for errors here, as LB_RESETCONTENT always
                //  returns true, and LB_DIR returns an error if the directory
                //  is empty.
                //
                SendMessage(lpCInfo->hFileLB, LB_RESETCONTENT, (WPARAM)NULL, (LPARAM)NULL);

                lstrcpy( szFiles, lpCInfo->CaptionBarText );
                lpHold = TStrChr(szFiles, TEXT('\0'));
                lpHold--;
                if( *lpHold != TEXT('\\') ){
                    lpHold++;
                    *lpHold = TEXT('\\');
                }

                lpHold++;
                lstrcpy( lpHold, TEXT("*.*"));

                if( SendMessage( lpCInfo->hFileLB, LB_DIR, (WPARAM)0x10,
                                 (LPARAM)szFiles ) == LB_ERR ){
                    LoadString(ghModule, IDS_FILDIRERR3, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    ReleaseMutex( lpCInfo->hFileMutex );
                    return(0);
                }

                //
                //  Set selection to first file.
                //
                if( SendMessage( lpCInfo->hFileLB, LB_SETSEL, (WPARAM)TRUE,
                                 (LPARAM)0 ) == LB_ERR ){
                    LoadString(ghModule, IDS_FILDIRERR4, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    ReleaseMutex( lpCInfo->hFileMutex );
                    return(0);
                }

                SetWindowText(lpCInfo->hTextWnd, lpCInfo->CaptionBarText);

                ReleaseMutex( lpCInfo->hFileMutex );

                return(1);
            }

            //
            //  Toggle active status of drive child.
            //
            case MM_TOGGLE:{

                SetWindowText(lpCInfo->hTextWnd, lpCInfo->CaptionBarText);
                return(1);
            }

            //
            // The following WM_COMMAND messages are sent by the listboxes
            //
            // HIWORD(wParam) = LB notification message
            // lParam = LB window handle
            //
            case LISTFILE_ID:{
              switch( HIWORD(wParam) ){
                //
                // In case of double click on a directory, expand the file
                // Listbox. if on a file name, run or edit file.
                //
                case LBN_DBLCLK:{
                    OpenListBoxItem(lpCInfo);
                    return(1);
                }
                break;

                case LBN_SETFOCUS:{
                    ghFocusWnd = lpCInfo->hFileLB;
                }
                break;

                default:
                    return(1);
              }
            } // LISTFILE_ID
            break;

            //
            // Notification from the Directory ListBox
            //
            case LISTDIR_ID:{
              switch( HIWORD(wParam) ){

                case LBN_SETFOCUS:{
                    ghFocusWnd = lpCInfo->hDirLB;
                }
                break;

                //
                // Expand subdirectories in dir listbox
                //
                case LBN_DBLCLK:{

                    if( !PostMessage(hwnd, WM_COMMAND, MM_FILLDIR,
                                     (LPARAM)0) ){
                        LoadString(ghModule, IDS_DBLCLKERR, lpBuffer, sizeof(lpBuffer));
                        ErrorMsg(lpBuffer);
                        return(0);
                    }
                    return(1);
                }
                break;

                case LBN_SELCHANGE:{
                    //
                    // for the Directory LB, fill the
                    // corresp. File LB with items in the newly selected dir.
                    //
                    LONG lIndex;

                    if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
                            == WAIT_TIMEOUT ){
                        LoadString(ghModule, IDS_SELCNGERR, lpBuffer, sizeof(lpBuffer));
                        ErrorMsg(lpBuffer);
                        return(0);
                    }

                    //
                    // Retrieve selected (careted) item.
                    //
                    lIndex = SendMessage( (HWND)lParam, LB_GETCARETINDEX,
                                        (WPARAM)NULL, (LPARAM)NULL );

                    if( !ConstructDirName(lpCInfo, lIndex,
                                          lpCInfo->CaptionBarText) ){
                        LoadString(ghModule, IDS_CONDIRERR, lpBuffer, sizeof(lpBuffer));
                        ErrorMsg(lpBuffer);
                        ReleaseMutex( lpCInfo->hDirMutex );
                        return(0);
                    }

                    ReleaseMutex( lpCInfo->hDirMutex );

                    if( !PostMessage(hwnd, WM_COMMAND, MM_FILLFILE,
                                     (LPARAM)0) ){
                        LoadString(ghModule, IDS_LBNOTFYERR, lpBuffer, sizeof(lpBuffer));
                        ErrorMsg(lpBuffer);
                        return(0);
                    }
                } // LBN_SELCHANGE
                break;

                default:
                    return(1);
              }
            } //  LISTDIR_ID
            break;

            default:
               return(1);
          }
        }
        break;

        //
        // Whenever the Drv child window is resized, its children has to be
        //  resized accordingly.  The GetWindowLong GWL_USERDATA values
        //  contain the height of the windows queried, set in their respective
        //  WM_CREATE cases.
        //
        case WM_SIZE: {
            LPCINFO     lpCInfo;

            int         nListHeight,
                        nListWidth;

            HWND        hLeftLB,
                        hRightLB;

            //
            // First, get the text window's handle from the per Drv child
            //  DRVCHILDINFO data structure
            //
            lpCInfo = (LPCINFO)GetWindowLong(hwnd, GWL_USERDATA);

            nListHeight = HIWORD(lParam) -
                          GetWindowLong(lpCInfo->hTextWnd, GWL_USERDATA)
                          - LIST_BORDER * 2;

            nListWidth = (LOWORD(lParam) - LIST_BORDER) / 2 - LIST_BORDER;

            //
            // Always, put the text window at the top of the Drv window.
            // Increasing sides and bottom extents by 1 to overlap border
            //   with Drv child border
            //
            MoveWindow(lpCInfo->hTextWnd,
                       -1,
                       0,
                       LOWORD(lParam) + 2,
                       GetWindowLong(lpCInfo->hTextWnd, GWL_USERDATA) + 1,
                       TRUE);

            if( lpCInfo->fDirLeft ){
                hLeftLB = lpCInfo->hDirLB;
                hRightLB = lpCInfo->hFileLB;
            }
            else{
                hLeftLB = lpCInfo->hFileLB;
                hRightLB = lpCInfo->hDirLB;
            }

            MoveWindow(hLeftLB,
                       LIST_BORDER,
                       GetWindowLong(lpCInfo->hTextWnd, GWL_USERDATA) + 1
                         + LIST_BORDER,
                       nListWidth,
                       nListHeight,
                       TRUE);

            MoveWindow(hRightLB,
                       (LOWORD(lParam) + LIST_BORDER) / 2,
                       GetWindowLong(lpCInfo->hTextWnd, GWL_USERDATA) + 1
                         + LIST_BORDER,
                       nListWidth,
                       nListHeight,
                       TRUE);

        break;
        }

        case WM_PARENTNOTIFY:{
            LPCINFO lpCInfo;

            if(wParam == WM_LBUTTONDOWN){
                lpCInfo = (LPCINFO) GetWindowLong(hwnd, GWL_USERDATA);
                if(lpCInfo == NULL){
                    LoadString(ghModule, IDS_PARNTFYERR, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    return(1);
                }
                if(HIWORD(wParam) == LISTDIR_ID)
                    SetFocus(lpCInfo->hDirLB);
                else
                    if(HIWORD(wParam) == LISTFILE_ID)
                        SetFocus(lpCInfo->hFileLB);
                    else
                        if(HIWORD(wParam) == TEXT_WINDOW_ID)
                            SetFocus(lpCInfo->hTextWnd);
            }

            break;
        }

        //
        // Same as MainWndProc's MM_ACTIVEDRV case.  The initial PostMessage
        //   is so the currently active Drv child will not process the message
        //   until it is no longer in focus.
        //
        case WM_MOUSEACTIVATE:{
            LPCINFO lpCInfo;

            PostMessage(ghActiveChild, WM_COMMAND, (WPARAM)MM_TOGGLE,
                        (LPARAM)NULL);
            ghActiveChild = hwnd;
            SendMessage(ghActiveChild, WM_COMMAND, (WPARAM)MM_TOGGLE,
                        (LPARAM)NULL);

            lpCInfo = (LPCINFO) GetWindowLong(hwnd, GWL_USERDATA);
            SendMessage(ghwndDrives, WM_COMMAND, MM_ACTIVEDRV,
                        (LPARAM)lpCInfo->lpDriveInfo);

            break;
        }

        //
        // Free the DRVCHILDINFO data that associates with this window
        //  also, reset the menu.
        //
        case WM_CLOSE: {
            LPCINFO lpCInfo;

            lpCInfo = (LPCINFO)GetWindowLong(hwnd, GWL_USERDATA);

            CloseHandle(lpCInfo->hDirMutex );
            CloseHandle(lpCInfo->hFileMutex );

            EnterCriticalSection(&gHeapCS);
            HeapFree(ghHeap, 0, (LPVOID)lpCInfo);
            LeaveCriticalSection(&gHeapCS);

            break;
        }

        default:
            return DefWindowProc(hwnd, message, wParam, lParam);

    } //switch
    return DefWindowProc(hwnd, message, wParam, lParam);
}

/***************************************************************************\
*
* GetLBText()
*
* Gets the text of the currently selected (careted) item in the given
*   listbox.
*
* Returns:  Index of selected item if successful, -1 on failure
*
* History:
* 4/26/93
*   Created.
*
\***************************************************************************/
LONG GetLBText(HWND hActiveLB, PTCHAR szItemBuff)
{
    LONG    lIndex;
    TCHAR   lpBuffer[128];

    //
    // Retrieve selected (careted) item.
    //
    lIndex = SendMessage( hActiveLB, LB_GETCARETINDEX,
                          (WPARAM)NULL, (LPARAM)NULL );

    if( SendMessage( hActiveLB, LB_GETTEXT, (WPARAM)lIndex,
                     (LPARAM)szItemBuff) == LB_ERR ){
        LoadString(ghModule, IDS_GETLBTXTERR, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);

return(-1);
    }

    return( lIndex );
}

/***************************************************************************\
*
* UpdateFileLB()
*
* Updates the file listbox of the drive child given by sending an MM_FILLFILE
*   message to it.
*
*  input:   hwnd    -   Handle of drive child to update file listbox of.
*
* History:
* 6/3/92
*   Created.
*
\***************************************************************************/
void UpdateFileLB(HWND hwnd)
{
    LPCINFO lpCInfo;

    lpCInfo = (LPCINFO)GetWindowLong(hwnd, GWL_USERDATA);

    SendMessage(hwnd, WM_COMMAND, (WPARAM)MM_FILLFILE,
                (LPARAM)0);
}

/***************************************************************************\
*
* OpenListBoxItem()
*
* Attempts to expand a selected File list box directory entry into an available
*  file listbox, or spawn a selected list box file.
*
*  input:   lpCInfo   -   pointer to Drv child's LPCINFO structure
*
* History:
* 5/27/92
*   Created.
*
\***************************************************************************/
BOOL OpenListBoxItem(LPCINFO lpCInfo)
{
    TCHAR   szItemBuff[DIRECTORY_STRING_SIZE];
    TCHAR   lpBuffer[128];

    //
    // Retrieve selected (careted) item.
    //
    GetLBText(lpCInfo->hFileLB, szItemBuff);

    //
    // Determine whether the item is a directory or a file.
    //   If file, Run if possible.
    //
    if( !IsDirectory(lpCInfo->CaptionBarText, szItemBuff) ){
        RunListBoxItem(lpCInfo);
        return(1);
        }

    //
    // It is a directory.  Set the new caption text, and expand files.
    //
    lstrcpy( lpCInfo->CaptionBarText, szItemBuff);

    if( !PostMessage(lpCInfo->hwnd, WM_COMMAND, MM_FILLFILE,
                        (LPARAM)0) ){
        LoadString(ghModule, IDS_OPENLBERR, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    return(1);
}

/***************************************************************************\
*
* RunListBoxItem()
*
* Attempts to spawn the selected list box file. If the file is not executable,
*   attempts to spawn an editor to edit the file.
*
*  input:   lpCInfo   -   pointer to Drv child's LPCINFO structure
*
* History:
* 5/27/92
*   Created.
*
* 5/12/93
*   Modified.
*
\***************************************************************************/
BOOL RunListBoxItem(LPCINFO lpCInfo)
{
    LONG        lIndex;
    TCHAR       szFileBuff[DIRECTORY_STRING_SIZE];
    TCHAR       szCmdLine[DIRECTORY_STRING_SIZE * 2];
    LPTSTR      szHold;
    TCHAR       lpBuffer[128];

    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    lstrcpy(szCmdLine, lpCInfo->CaptionBarText);

    lstrcat(szCmdLine, TEXT("\\"));

    //
    // Get file that was opened, and attempt to spawn. If fails, attempt to
    // edit it.
    //

    //
    // Retrieve selected (careted) item.
    //
    lIndex = GetLBText(lpCInfo->hFileLB, szFileBuff);
    if( lIndex == LB_ERR)
        return(0);

    //
    // Don't assume the file has an extension. if no '.', insert one at end
    //  of file, so spawned editor will not assume any default extension.
    //  (i.e. Notepad assumes a .TXT if no extension is given.)
    //
    szHold = TStrChr(szFileBuff, TEXT('.'));
    if( !szHold ){
        szHold = TStrChr(szFileBuff, TEXT('\0'));
        *szHold = TEXT('.');
        *(szHold + sizeof(TCHAR)) = TEXT('\0');
    }

    si.cb = sizeof(STARTUPINFO);
    si.lpReserved = NULL;
    si.lpDesktop = NULL;
    si.lpTitle = NULL;
    si.dwFlags = 0;
    si.cbReserved2 = 0;
    si.lpReserved2 = NULL;

    //
    // Convert file to uppercase for extension comparison.  Raise the
    //   priority of this thread for the duration of the create process code.
    //   This is so the CreateProcess will not be held up by the directory
    //   fill threads.
    //   If an executable extension, spawn, else edit.
    //
    CharUpper(szFileBuff);

    SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

    for(lIndex = 0; lIndex < NUM_EXTENSION_STRINGS; lIndex++)
        if( !lstrcmp(szHold, &gszExtensions[lIndex][0]) ){

            lstrcat(szCmdLine, szFileBuff);

            if( !CreateProcess(NULL, (LPTSTR)szCmdLine, NULL, NULL, FALSE,
                               CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS,
                               NULL, lpCInfo->CaptionBarText, &si, &pi) ){
                LoadString(ghModule, IDS_CANTSPAWN, lpBuffer, sizeof(lpBuffer));
                ErrorMsg(lpBuffer);
                return(0);
            }

            CloseHandle( pi.hProcess );
            CloseHandle( pi.hThread );

            return(1);
        }

    LoadString(ghModule, STR_DEF_EDITOR, szCmdLine,
               DIRECTORY_STRING_SIZE * 2);
    lstrcat(szCmdLine, TEXT(" "));
    lstrcat(szCmdLine, szFileBuff);

    if( !CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE,
                       CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS,
                       NULL, lpCInfo->CaptionBarText, &si, &pi) ){
        LoadString(ghModule, IDS_CANTEDIT, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );

    SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL);

    return(1);
}

/***************************************************************************\
*
* FilerGetVersion()
*
*  Given an item from a File ListBox, GetVersion retrieves the version
*    information from the specified file.
*
*  input:   lpszFileName   -   the name of the file.
*           dwBuffSize     -   > 0 size of buffer to hold version info
*           szBuff         -   buffer to hold version info
*
*  returns: TRUE if successful, FALSE otherwise.
*
*  comments:  gVKArray would need a critical section if this function were
*             to be called by more than one thread.
*
* History:
* 2/23/93
*   Created.
*
\***************************************************************************/
BOOL FilerGetVersion(LPTSTR lpszFileName, DWORD dwBuffSize, LPTSTR szBuff)
{
    //
    // NUM_VERSION_INFO_KEYS in GLOBALS.H should be set to the number of entries in
    //   VersionKeys[].
    //
    CONST static TCHAR   *VersionKeys[] = {
            TEXT("ProductName"),
            TEXT("ProductVersion"),
            TEXT("OriginalFilename"),
            TEXT("FileDescription"),
            TEXT("FileVersion"),
            TEXT("CompanyName"),
            TEXT("LegalCopyright"),
            TEXT("LegalTrademarks"),
            TEXT("InternalName"),
            TEXT("PrivateBuild"),
            TEXT("SpecialBuild"),
            TEXT("Comments")
    };

    static TCHAR szNull[1] = TEXT("");
    LPVOID  lpInfo;
    DWORD   cch;
    UINT    i;
    TCHAR   key[80];
    TCHAR   lpBuffer[10];

    GetFileVersionInfo(lpszFileName, 0, dwBuffSize, (LPVOID)szBuff );
    wsprintf(lpBuffer, "%04X", GetUserDefaultLangID());
strcat(lpBuffer,TEXT("04B0"));
    for (i = 0; i < NUM_VERSION_INFO_KEYS; i++) {
        lstrcpy(key, VERSION_INFO_KEY_ROOT);
        lstrcat(key, lpBuffer);
        lstrcat(key, "\\");
        lstrcat(key, VersionKeys[i]);
        gVKArray[i].szKey = VersionKeys[i];

        //
        // If version info exists, and the key query is successful, add
        //  the value.  Otherwise, the value for the key is NULL.
        //
        if( dwBuffSize && VerQueryValue(szBuff, key, &lpInfo, &cch) )
            gVKArray[i].szValue = lpInfo;
        else
            gVKArray[i].szValue = szNull;
    }

    return TRUE;
}

/***************************************************************************\
* VersionProc()
*
* .EXE Version Info Dialog Box
*
* History:
* 2/24/93  2am
*   Created.
\***************************************************************************/
LRESULT WINAPI VersionProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static LPTSTR   lpszBuff;
    TCHAR   lpBuffer[128];

    switch (message){
      case WM_INITDIALOG:{

        LPCINFO lpCInfo;
        DWORD   dwLength;
        DWORD   dwCount;
        DWORD   dwHandle;
        TCHAR   szFile[DIRECTORY_STRING_SIZE];
        TCHAR   szDir[DIRECTORY_STRING_SIZE];
        TCHAR   szFullName[DIRECTORY_STRING_SIZE];
        LPTSTR  lpszHold;
        TCHAR   lpBuffer[128];

        lpCInfo = (LPCINFO)lParam;
        LoadString(ghModule, IDS_FILE, lpBuffer, sizeof(lpBuffer));
        strcpy(szFile, lpBuffer);
        LoadString(ghModule, IDS_DIR, lpBuffer, sizeof(lpBuffer));
        strcpy(szDir, lpBuffer);

        //
        // Concatenate path to szDir text-control string.
        //
        lstrcat(szDir, lpCInfo->CaptionBarText);
        SendDlgItemMessage( hDlg, SB_DEST, WM_SETTEXT,
                            (WPARAM)0, (LPARAM)szDir);

        //
        // Concatenate File name to szFile text-control string.
        //
        dwLength = lstrlen(szFile);

        //
        // Get selected (careted) ListBox item
        //
        GetLBText(lpCInfo->hFileLB, (PTCHAR)&szFile[dwLength]);

        SendDlgItemMessage( hDlg, SB_SOURCE, WM_SETTEXT,
                            (WPARAM)0, (LPARAM)szFile);

        //
        // Fill Version Key and Value edit boxes.
        //
        lstrcpy( szFullName, lpCInfo->CaptionBarText );

        //
        // a file under some file systems may have [] characters.
        //   Prepend path, adding a delimiting backslash unless we're in the root.
        //   If the attribute check is successful, it's a file, so leave.
        //
        lpszHold = TStrChr(szFullName, TEXT('\0'));

        lpszHold--;
        if( *lpszHold != TEXT('\\') ){
            lpszHold++;
            *lpszHold = TEXT('\\');
        }
        lpszHold++;

        lstrcpy(lpszHold, &szFile[dwLength]); // File name past 'File:' prefix

        dwLength = GetFileVersionInfoSize( szFullName, &dwHandle);
        if( !dwLength ){
            LoadString(ghModule, IDS_GETVERERR, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(1);
        }

        // Allocate Version Info buffer
        EnterCriticalSection(&gHeapCS);
        lpszBuff = (LPTSTR)HeapAlloc( ghHeap, HEAP_ZERO_MEMORY, dwLength * sizeof(TCHAR) );
        LeaveCriticalSection(&gHeapCS);

        FilerGetVersion( szFullName, dwLength, lpszBuff );

        for( dwCount = 0; dwCount < NUM_VERSION_INFO_KEYS; dwCount++){
            if( SendDlgItemMessage( hDlg, SB_KEY, LB_ADDSTRING, 0,
                                    (LPARAM)gVKArray[dwCount].szKey)
                                    == LB_ERR ){
                LoadString(ghModule, IDS_GETVERERR2, lpBuffer, sizeof(lpBuffer));
                ErrorMsg(lpBuffer);
                return(0);
            }
            if( SendDlgItemMessage( hDlg, SB_VALUE, LB_ADDSTRING, 0,
                                    (LPARAM)gVKArray[dwCount].szValue)
                                    == LB_ERR ){
                LoadString(ghModule, IDS_GETVERERR3, lpBuffer, sizeof(lpBuffer));
                ErrorMsg(lpBuffer);
                return(0);
            }
        }

        //  Set selection in listboxes to first item.
        if( SendDlgItemMessage( hDlg, SB_KEY, LB_SETCURSEL,
                               (WPARAM)0,
                               (LPARAM)0 )
                               == LB_ERR ){
            LoadString(ghModule, IDS_GETVERERR4, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(0);
        }
        if( SendDlgItemMessage( hDlg, SB_VALUE, LB_SETCURSEL,
                               (WPARAM)0,
                               (LPARAM)0 )
                               == LB_ERR ){
            LoadString(ghModule, IDS_GETVERERR5, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(0);
        }

        //  Initialize Scroll Bar
        //  Check to see if a scroll bar is needed. See GLOBALS.H and FILER.DLG
        if( NUM_VERSION_INFO_KEYS - VERSION_DLG_LB_HEIGHT > 0 )
            SendDlgItemMessage( hDlg, SB_SCROLL, SBM_SETRANGE,
                               (WPARAM)0,
                               (LPARAM)NUM_VERSION_INFO_KEYS -1);
        SendDlgItemMessage( hDlg, SB_SCROLL, SBM_SETPOS,
                           (WPARAM)0,
                           (LPARAM)TRUE );

        return(1);
      }

      case WM_VSCROLL:{
        int nPos;

        nPos = SendDlgItemMessage( hDlg, SB_SCROLL, SBM_GETPOS,
                                  (WPARAM)0,
                                  (LPARAM)0 );

        switch( LOWORD(wParam) ){

            case SB_PAGEDOWN:{
                nPos += VERSION_DLG_LB_HEIGHT;
                if( nPos > NUM_VERSION_INFO_KEYS )
                    nPos = NUM_VERSION_INFO_KEYS;
                break;
            }
            case SB_LINEDOWN:{
                nPos++;
                if( nPos > NUM_VERSION_INFO_KEYS )
                    nPos = NUM_VERSION_INFO_KEYS;
                break;
            }
            case SB_PAGEUP:{
                nPos -= VERSION_DLG_LB_HEIGHT;
                if( nPos < 0 )
                    nPos = 0;
                break;
            }
            case SB_LINEUP:{
                nPos--;
                if( nPos < 0 )
                    nPos = 0;
                break;
            }
            default:
                return(1);
        }

        //  Set Scroll Bar position
        SendDlgItemMessage( hDlg, SB_SCROLL, SBM_SETPOS,
                            (WPARAM)nPos,
                            (LPARAM)TRUE );
        //  Set selection in listboxes
        if( SendDlgItemMessage( hDlg, SB_KEY, LB_SETCURSEL,
                                (WPARAM)nPos,
                                (LPARAM)0 )
                                == LB_ERR ){
            LoadString(ghModule, IDS_GETVERERR6, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(0);
        }
        if( SendDlgItemMessage( hDlg, SB_VALUE, LB_SETCURSEL,
                                (WPARAM)nPos,
                                (LPARAM)0 )
                                == LB_ERR ){
            LoadString(ghModule, IDS_GETVERERR6, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(0);
        }

        return(0);
      }

      case WM_CLOSE:
          // Free allocated buffer
          EnterCriticalSection(&gHeapCS);
          HeapFree( ghHeap, 0, lpszBuff);
          LeaveCriticalSection(&gHeapCS);

          EndDialog(hDlg, wParam);
          return(1);

      case WM_COMMAND:{
            int nLBid = 0;  // holds ID of listbox other than one sending LBN msg.

        switch( LOWORD(wParam) ){
            case SB_OK:
            case SB_CANCEL:{
                // Free allocated buffer
                EnterCriticalSection(&gHeapCS);
                HeapFree( ghHeap, 0, lpszBuff);
                LeaveCriticalSection(&gHeapCS);

                EndDialog(hDlg, wParam);
                return(1);
            }

            // SB_VALUE and SB_KEY are the ListBox IDs. Below are LB notifications.
            case SB_VALUE:
                nLBid = SB_KEY;
                // we slip through to the SB_KEY case on purpose!!
            case SB_KEY:{
                int nSelect;    // Holds selected item

                if( HIWORD(wParam) == LBN_SELCHANGE ){

                    if( !nLBid )    // If nLBid wasn't set by the SB_VALUE case above...
                        nLBid = SB_VALUE;

                    //  Get current listbox selection.
                    nSelect = SendDlgItemMessage( hDlg, LOWORD(wParam), LB_GETCURSEL,
                                        (WPARAM)0,
                                        (LPARAM)0 );
                    if( nSelect == LB_ERR ){
                        LoadString(ghModule, IDS_GETVERERR7, lpBuffer, sizeof(lpBuffer));
                        ErrorMsg(lpBuffer);
                        return(0);
                    }

                    //  Set similar selection in corresponding listbox.
                    if( SendDlgItemMessage( hDlg, nLBid, LB_SETCURSEL,
                                            (WPARAM)nSelect,
                                            (LPARAM)0 )
                                            == LB_ERR ){
                        LoadString(ghModule, IDS_GETVERERR8, lpBuffer, sizeof(lpBuffer));
                        ErrorMsg(lpBuffer);
                        return(0);
                    }

                    //  Set Scroll Bar position
                    SendDlgItemMessage( hDlg, SB_SCROLL, SBM_SETPOS,
                                        (WPARAM)nSelect,
                                        (LPARAM)TRUE );
                }
            }
        }
        return(1);
      }
    }

    return(0);
}

/***************************************************************************\
*
* IsDirectory()
*
*  Given an item from a ListBox filled from an LB_DIR call, IsDirectory
*    verifies whether or not the item is a directory, and if so, returns
*    true, and places the full directory path in lpszFile.
*
*  input:   lpszDir     -   Holds current directory
*           lpszFile    -   Holds item of dubious directoryness.
*
* History:
* 5/30/92
*   Created.
*
\***************************************************************************/
BOOL IsDirectory(LPTSTR lpszDir, LPTSTR lpszFile)
{
    DWORD   dwAttrib;
    LPTSTR  lpszHold;
    TCHAR   szItem[DIRECTORY_STRING_SIZE * 2];
    TCHAR   lpBuffer[128];

    //
    // if it's '..', go up one directory
    //
    if( !lstrcmp(lpszFile, TEXT("[..]")) ){
        lstrcpy(lpszFile, lpszDir);
        lpszHold = TStrChr(lpszFile, TEXT('\0'));
        while( lpszHold > lpszFile ){
            if(*lpszHold == TEXT('\\')){
                break;
            }
            lpszHold = CharPrev(lpszFile, lpszHold);
        }
        if(lpszHold <= lpszFile){
            LoadString(ghModule, IDS_ISDIRERR, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(0);
        }
        else{
            if( TStrChr(lpszFile, TEXT('\\')) == lpszHold )
                lpszHold++;
            *lpszHold = TEXT('\0');
            return(1);
        }
    }

    //
    // A directory will have [] around it in the listbox. Check for it.
    //
    if( *lpszFile != TEXT('[') )
        return(0);

    //
    // A file under some file systems may have [] characters.
    //   Prepend path, adding a delimiting backslash unless we're in the root.
    //   If the attribute check is successful, it's a file, so leave.
    //
    // NOTE:  This is a hack.  If there is a file called '[foo]' and a
    //   Directory called 'foo', they will appear identical in the listbox.
    //   Rather than check for this rare case, if it happens, Filer will
    //   assume it is a file first, as the directory may be changed from
    //   the Directory Listbox.
    //
    lstrcpy(szItem, lpszDir);
    lpszHold = TStrChr(szItem, TEXT('\0'));

    lpszHold--;
    if( *lpszHold != TEXT('\\') ){
        lpszHold++;
        *lpszHold = TEXT('\\');
    }
    lpszHold++;

    lstrcpy(lpszHold, lpszFile);

    dwAttrib = GetFileAttributes(szItem);

    if( dwAttrib != 0xFFFFFFFF )             // there really is a file called
        return(0);                           //  '[foo]'.  Exit.

    //
    // remove the [], and check if valid directory.
    //   if it fails, or it's not a directory, leave.
    //
    lstrcpy(lpszHold, &lpszFile[1]);
    lpszHold = TStrChr(lpszHold, TEXT('\0'));

    lpszHold--;
    if( *lpszHold != TEXT(']') )
        return(0);
    *lpszHold = TEXT('\0');

    dwAttrib = GetFileAttributes(szItem);

    if( (dwAttrib == 0xFFFFFFFF) || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) )
        return(0);

    //
    // OK, it's a directory, and szItem now holds the fully qualified path.
    // copy this to the filename buffer sent in, and return true.
    //
    CharUpper(szItem);
    lstrcpy(lpszFile, szItem);
    return(1);
}

/***************************************************************************\
*
* ExecuteFileAction()
*
* Creates a dialog box verifying a file action, and carries out the action.
*
*  input:   hwnd    -   handle to Drv child.
*           lpSelect    -   pointer to SELECTINFO structure, containing
*                           info on the file i/o action to be performed.
*
* History:
* 5/28/92
*   Created.
*
\***************************************************************************/
BOOL ExecuteFileAction(LPSINFO lpSelect)
{
    TCHAR   lpBuffer[128];

    if( DialogBoxParam(ghModule, TEXT("SelectDlg"), lpSelect->hwnd,
                  (DLGPROC)SelectProc, (LPARAM)lpSelect) == -1 ){
        LoadString(ghModule, IDS_EXEFILEERR, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }
    return(1);
}

/***************************************************************************\
* SelectProc()
*
* File I/O selection dialog proc.
*
* History:
* 5/28/92
*   Created.
\***************************************************************************/
LRESULT WINAPI SelectProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static LPSINFO lpSelect;
    TCHAR   lpBuffer[128];

    switch (message) {
      case WM_INITDIALOG:{

        lpSelect = (LPSINFO)lParam;

        //
        // Fill source and destination fields of Select dialog.
        //
        if( !FillSelectDlg(hDlg, lpSelect) ){
            EndDialog(hDlg, wParam);
            return(1);
        }

        //
        // Set the TEXT('action') text (i.e. TEXT("COPYING:"), TEXT("MOVING:"), etc.)
        //
        if( !SetDlgItemText(hDlg, SB_ACTION, lpSelect->szAction) ){
            LoadString(ghModule, IDS_SELPROCERR, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            EndDialog(hDlg, wParam);
            return(1);
        }

        //
        // Set the TEXT("TO:") or TEXT("FROM:") text.
        //
        if( !SetDlgItemText(hDlg, SB_TOFROM, lpSelect->szToFrom) ){
            LoadString(ghModule, IDS_SELPROCERR, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            EndDialog(hDlg, wParam);
            return(1);
        }

        break;
      }
      case WM_CLOSE:
        EndDialog(hDlg, wParam);
        return(1);

      case WM_COMMAND:{
        switch(LOWORD(wParam)){
            case SB_OK:{
                DoFileIO(hDlg, lpSelect);
                EndDialog(hDlg, wParam);
                return(1);
            }
            case SB_CANCEL:{
                EndDialog(hDlg, wParam);
                return(1);
            }
        }
        return(1);
      }
    }

    return(0);
}

/***************************************************************************\
*
* FillSelectDlg()
*
* Fills the Select Dialog box with the files selected in the active Drv Child
*  for a file i/o action.  Destination defaults to directory selection in
*  inactive Drv Child.
*
*  input:   hDlg    -   handle to Select dialog box.
*           hwnd    -   handle to Drv child.
*
* History:
* 5/28/92
*   Created.
*
\***************************************************************************/
BOOL FillSelectDlg(HWND hDlg, LPSINFO lpSelect)
{
    LONG    lCount;         // Number of items selected in ListBox
    LONG    lSize;          // Holds size of a string
    LONG    lLargest = 0;   // Holds largest string encountered.
    UINT    *lpnIndex;      // ptr to array of selected ListBox items' indeces
    int     i;              // counter
    TCHAR   lpBuffer[128];  // Buffer for string retrieved from resources

    LPCINFO lpCInfo;
    HWND    hDest;          // dir of files if delete, else inactive Drv dir.

    HDC     hDC;
    TEXTMETRIC  Metrics;

    LPTSTR  lpszHold;       // marks end of directory path in szName.
    TCHAR   szName[DIRECTORY_STRING_SIZE * 2];  // holds ListBox strings.

    lpCInfo = (LPCINFO)GetWindowLong(lpSelect->hwnd, GWL_USERDATA);

    lCount = SendMessage( lpCInfo->hFileLB, LB_GETSELCOUNT,
                          (WPARAM)NULL, (LPARAM)NULL );

    //
    // if no items selected, leave.
    //
    if( !lCount )
        return(0);

    //
    // Allocate array of lCount listbox indexes, and fill it.
    //
    EnterCriticalSection(&gHeapCS);
    lpnIndex = (UINT*)HeapAlloc( ghHeap, HEAP_ZERO_MEMORY, lCount * sizeof(UINT) );
    LeaveCriticalSection(&gHeapCS);
    if( !lpnIndex ){
        LoadString(ghModule, IDS_SELDLGERR, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    if( SendMessage( lpCInfo->hFileLB, LB_GETSELITEMS, (WPARAM)lCount,
                     (LPARAM)lpnIndex) != lCount ){
        LoadString(ghModule, IDS_SELDLGERR2, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    //
    // Check if each selected entry is a valid file.
    //
    // Check to see if there is a terminating backslash, by decrementing
    //  pointer, checking, adding a '\' if necessary, then re-incrementing
    //  the pointer.
    //
    lstrcpy(szName, lpCInfo->CaptionBarText);

    lpszHold = TStrChr(szName, TEXT('\0'));

    lpszHold--;
    if( *lpszHold != TEXT('\\') ){
        lpszHold++;
        *lpszHold = TEXT('\\');
    }
    lpszHold++;

    //
    // Fill Dlg ListBox with selected strings from Drv child's ListBox,
    //   noting size of largest entry.
    //
    for( i = 0; i < lCount; i++){
        if( SendMessage( lpCInfo->hFileLB, LB_GETTEXT, (WPARAM)lpnIndex[i],
                        (LPARAM)lpszHold) == LB_ERR ){
            LoadString(ghModule, IDS_SELDLGERR3, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(0);
        }

        if( GetFileAttributes(szName) == 0xFFFFFFFF ){    //Error
            LoadString(ghModule, IDS_SELDLGERR4, lpBuffer, sizeof(lpBuffer));
            lstrcat(lpszHold, lpBuffer);
            ErrorMsg(lpszHold);
        }
        else{
            lSize = lstrlen(lpszHold);
            if( lSize > lLargest )
                lLargest = lSize;

            if( SendDlgItemMessage( hDlg, SB_SOURCE, LB_ADDSTRING, 0,
                                    (LPARAM)lpszHold) == LB_ERR ){
                LoadString(ghModule, IDS_SELDLGERR5, lpBuffer, sizeof(lpBuffer));
                ErrorMsg(lpBuffer);
                return(0);
            }
        }
    }

    //
    // Get the average char width of current font,
    // We then set the dialog listbox column width to
    // (longest string + arbitrary column spacing) * ave width.
    //
    hDC = GetDC(lpSelect->hwnd);
    if( !GetTextMetrics(hDC, &Metrics) ){
        LoadString(ghModule, IDS_SELDLGERR6, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    ReleaseDC(lpSelect->hwnd, hDC);

    SendDlgItemMessage( hDlg, SB_SOURCE, LB_SETCOLUMNWIDTH,
                        (WPARAM)((lLargest + 8) * Metrics.tmAveCharWidth),
                        (LPARAM)NULL );

    EnterCriticalSection(&gHeapCS);
    HeapFree( ghHeap, 0, (LPVOID)lpnIndex);
    LeaveCriticalSection(&gHeapCS);

    //
    // Fill SB_DEST with directory.
    // If deleteing, default to the directory of the files.
    //
    if( lpSelect->dwAction != MM_DELETE ){
        //
        // Not Deleting.  Default to selected directory (titlebar)
        //   of the inactive Drive child.
        //
        if( lpSelect->hwnd == ghwndDrv1 )
            hDest = ghwndDrv2;
        else
            hDest = ghwndDrv1;

        lpCInfo = (LPCINFO)GetWindowLong(hDest, GWL_USERDATA);
    }

   SendDlgItemMessage( hDlg, SB_DEST, EM_LIMITTEXT,
                            (WPARAM)DIRECTORY_STRING_SIZE, (LPARAM)0);

   if( SendDlgItemMessage( hDlg, SB_DEST, WM_SETTEXT, 0,
                            (LPARAM)lpCInfo->CaptionBarText) == CB_ERR ){
        LoadString(ghModule, IDS_SELDLGERR7, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    return(1);
}

/***************************************************************************\
*
* DoFileIO()
*
* Called by SelectProc.  Carries out the chosed file operations verified
*   in the Select Dialog Box.
*
*  input:   hDlg    -   handle to Select dialog box.

*           lpSelect    -   ptr to SELECTINFO structure.
*
*  returns: 1 if successful
*           0 if unsuccessful
* History:
* 6/3/92
*   Created.
*
\***************************************************************************/
BOOL DoFileIO( HWND hDlg, LPSINFO lpSelect)
{
    LONG    lCount;
    LONG    lFile;
    LPCINFO lpCInfo;
    LPTSTR  lpEndDest;
    LPTSTR  lpEndSource;
    BOOL    fError = FALSE;
    TCHAR   lpBuffer[128];

    TCHAR   szSource[DIRECTORY_STRING_SIZE];
    TCHAR   szDest[DIRECTORY_STRING_SIZE * 2];

    //
    // Find number of files to copy
    //
    lCount = SendDlgItemMessage( hDlg, SB_SOURCE, LB_GETCOUNT, (WPARAM)NULL,
                                 (LPARAM)NULL);

    if( (lCount == LB_ERR) || (lCount == 0) ){
        LoadString(ghModule, IDS_FILEIOERR, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    //
    // Get source directory path. Add a \ to the end, set pointer to end.
    //
    lpCInfo = (LPCINFO)GetWindowLong( lpSelect->hwnd, GWL_USERDATA);

    lstrcpy(szSource, lpCInfo->CaptionBarText);

    lpEndSource = TStrChr(szSource, TEXT('\0'));
    *lpEndSource++ = TEXT('\\');

    //
    // Get destination directory path. Add a \ to the end, set pointer to end.
    //
    if( SendDlgItemMessage( hDlg, SB_DEST, WM_GETTEXT,
            (WPARAM)DIRECTORY_STRING_SIZE * 2, (LPARAM)szDest) == CB_ERR ){
        LoadString(ghModule, IDS_FILEIOERR2, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    lpEndDest = TStrChr(szDest, TEXT('\0'));
    *lpEndDest++ = TEXT('\\');

    //
    // for each file, do appropriate I/O.
    //
    while( lCount ){

        lCount--;

        lFile = SendDlgItemMessage( hDlg, SB_SOURCE, LB_GETTEXT,
                                    (WPARAM)0, (LPARAM)lpEndSource);
        if( lFile == LB_ERR){
            LoadString(ghModule, IDS_FILEIOERR3, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            fError = TRUE;
        }

        SendDlgItemMessage( hDlg, SB_SOURCE, LB_DELETESTRING,
                            (WPARAM)0, (LPARAM)NULL);

        switch( lpSelect->dwAction ){
          case MM_COPY:{

            lstrcpy(lpEndDest, lpEndSource);

            if( !CopyFile( szSource, szDest, TRUE) )
                if( !HandleIOError(lpSelect->hwnd, lpSelect->dwAction,
                                   szSource, szDest) )
                    fError = TRUE;
            break;
          }

          case MM_MOVE:{

            lstrcpy(lpEndDest, lpEndSource);

            if( !MoveFileEx( szSource, szDest,
                            MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED) )
                if( !HandleIOError(lpSelect->hwnd, lpSelect->dwAction,
                                   szSource, szDest) )
                    fError = TRUE;
            break;
          }
          case MM_DELETE:{

            if(GetFileAttributes(szSource) & FILE_ATTRIBUTE_DIRECTORY) {
                if(!RemoveDirectory(szSource))
                    if( !HandleIOError(lpSelect->hwnd, lpSelect->dwAction,
                                   szSource, szDest) )
                        fError = TRUE;
            }
            else {
                if( !DeleteFile(szSource) )
                    if( !HandleIOError(lpSelect->hwnd, lpSelect->dwAction,
                                   szSource, szDest) )
                        fError = TRUE;
            }
          }
          break;
        }
        if( fError )
            break;
    }

    //
    //  Update file listboxes.
    //
    if( fError )
        return(0);
    else{
        UpdateFileLB(ghwndDrv1);
        UpdateFileLB(ghwndDrv2);
    }

    return(1);
}

/***************************************************************************\
*
* HandleIOError()
*
* Called by DoFileIO. Handles errors arising from File IO operations
*
*  input:   hwnd        -   handle to Drv child window
*           dwAction    -   File I/O Action.  Window message.
*           szSource    -   ptr to Source string
*           szDest      -   ptr to Destination string
*
*  returns: 1 if successful
*           0 if unsuccessful
*
* History:
* 6/3/92
*   Created.
*
\***************************************************************************/
BOOL HandleIOError(HWND hwnd, DWORD dwAction, LPTSTR szSource, LPTSTR szDest)
{
    DWORD   dwError;
    int     nReply;
    LPCINFO lpCInfo;
    TCHAR   buff[50];
    TCHAR   lpBuffer[128];

    dwError = GetLastError();

    CharUpper(szDest);

    switch( dwError ){
        case ERROR_ALREADY_EXISTS:
        case ERROR_FILE_EXISTS:{
          //
          // MoveFile file already exists. We can CopyFile & DeleteFile source.
          //
          LoadString(ghModule, IDS_HNDIOMSG, lpBuffer, sizeof(lpBuffer));
          nReply = MessageBox(hwnd,
                              lpBuffer,
                              szDest, MB_YESNOCANCEL);
          switch( nReply ){
            case IDYES:{

              lpCInfo = (LPCINFO)GetWindowLong(hwnd, GWL_USERDATA);

              if( !CopyFile( szSource, szDest, FALSE) )
                  if( HandleIOError(hwnd, dwAction, szSource, szDest) )
                      return(1);
                  else
                      return(0);
              if( dwAction == MM_MOVE )
                  if( !DeleteFile( szSource) )
                      if( HandleIOError(hwnd, dwAction, szSource, szDest) )
                          return(1);
                      else
                          return(0);

              UpdateFileLB(ghwndDrv1);
              UpdateFileLB(ghwndDrv2);

              return(1);
            }
            case IDNO:{
              return(1);
            }
            case IDCANCEL:{
              return(0);
            }
          }
          break;
        }

        case ERROR_ACCESS_DENIED:
        case ERROR_FILE_NOT_FOUND:
        case ERROR_INVALID_PARAMETER:{
            LoadString(ghModule, IDS_HNDIOMSG2, lpBuffer, sizeof(lpBuffer));
            wsprintf(buff,
                     lpBuffer,
                     dwError);
            MessageBox( hwnd, buff, szDest, MB_OK);

            return(1);
        }

        case ERROR_DISK_FULL:{
            LoadString(ghModule, IDS_HNDIOMSG3, lpBuffer, sizeof(lpBuffer));
            MessageBox( hwnd,
            lpBuffer,
            szDest, MB_OK);
            return(0);
        }

        case ERROR_INVALID_NAME:
        case ERROR_DIRECTORY:
        case ERROR_PATH_NOT_FOUND:{
            LoadString(ghModule, IDS_HNDIOMSG4, lpBuffer, sizeof(lpBuffer));
            MessageBox( hwnd,
            lpBuffer,
            szDest, MB_OK);
            return(0);
        }

        default:{
            LoadString(ghModule, IDS_HNDIOMSG5, lpBuffer, sizeof(lpBuffer));
            wsprintf(buff, lpBuffer, dwError);
            ErrorMsg(buff);
            return(1);
        }
    }

    return(1);
}

/***************************************************************************\
* RenameProc()
*
* File I/O rename Dialog Box
*
* History:
* 6/5/92  2am
*   Created.
\***************************************************************************/
LRESULT WINAPI RenameProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static TCHAR   szName[DIRECTORY_STRING_SIZE];
    static LPTSTR  lpszHold;
    static LPCINFO lpCInfo;
    TCHAR   lpBuffer[128];
    TCHAR   lpBuffer2[128];

    switch (message){
      case WM_INITDIALOG:{

        LONG    lIndex;

        lpCInfo = (LPCINFO)lParam;

        //
        // Check to see if there is a terminating backslash, by decrementing
        //  pointer, checking, adding a '\' if necessary, then re-incrementing
        //  the pointer.
        //
        lstrcpy(szName, lpCInfo->CaptionBarText);
        lpszHold = TStrChr(szName, TEXT('\0'));

        lpszHold--;
        if( *lpszHold != TEXT('\\') ){
            lpszHold++;
            *lpszHold = TEXT('\\');
        }
        lpszHold++;

        lIndex = GetLBText(lpCInfo->hFileLB, lpszHold);
        if( lIndex == LB_ERR )
            return(1);

        //
        // if not a valid file (i.e. listbox directory entry in []s), fail.
        //
        if( GetFileAttributes(szName) == 0xFFFFFFFF ){    //Error
            LoadString(ghModule, IDS_SELDLGERR4, lpBuffer, sizeof(lpBuffer));
            lstrcat(lpszHold, lpBuffer);
            ErrorMsg(lpszHold);
            return(1);
        }

        //
        // Place name in both source and destination edit controls.
        //
        CharUpper(szName);

        SendDlgItemMessage( hDlg, SB_SOURCE, EM_LIMITTEXT,
                            (WPARAM)DIRECTORY_STRING_SIZE, (LPARAM)0);

        SendDlgItemMessage( hDlg, SB_DEST, EM_LIMITTEXT,
                            (WPARAM)DIRECTORY_STRING_SIZE, (LPARAM)0);

        if( SendDlgItemMessage( hDlg, SB_SOURCE, WM_SETTEXT, 0,
                                (LPARAM)lpszHold) == LB_ERR ){
            LoadString(ghModule, IDS_RENMEMSG, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(1);
        }
        if( SendDlgItemMessage( hDlg, SB_DEST, WM_SETTEXT, 0,
                                (LPARAM)lpszHold) == LB_ERR ){
            LoadString(ghModule, IDS_RENMEMSG2, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(1);
        }
        return(1);
      }
      case WM_COMMAND:{
        switch(LOWORD(wParam)){
            case SB_OK:{
                TCHAR   szDest[DIRECTORY_STRING_SIZE];
                LPTSTR  lpszCheck;

                //
                // the entire source dir\file is still in static szName.
                // static lpszHold points to the end of the dir
                //
                *lpszHold = TEXT('\0');

                lstrcpy(szDest, szName);

                if( SendDlgItemMessage( hDlg, SB_SOURCE, WM_GETTEXT,
                                        DIRECTORY_STRING_SIZE,
                                        (LPARAM)lpszHold) == LB_ERR ){
                    LoadString(ghModule, IDS_RENMEMSG3, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    EndDialog(hDlg, wParam);
                    return(1);
                }

                //
                // If there are any \s in the new filename, fail.
                //
                lpszCheck = TStrChr(lpszHold, TEXT('\\'));

                if( lpszCheck ){
                    LoadString(ghModule, IDS_RENMEMSG4, lpBuffer, sizeof(lpBuffer));
                    LoadString(ghModule, IDS_RENMEMSG5, lpBuffer2, sizeof(lpBuffer2));
                    MessageBox(lpCInfo->hwnd,
                               lpBuffer,
                               lpBuffer2, MB_OK );
                    return(1);
                }

                lpszHold = TStrChr(szDest, TEXT('\0'));

                if( SendDlgItemMessage( hDlg, SB_DEST, WM_GETTEXT,
                                        DIRECTORY_STRING_SIZE,
                                        (LPARAM)lpszHold) == LB_ERR ){
                    LoadString(ghModule, IDS_RENMEMSG6, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    EndDialog(hDlg, wParam);
                    return(1);
                }

                //
                // If there are any \s in the destination filename, fail.
                //
                lpszCheck = TStrChr(lpszHold, TEXT('\\'));

                if( lpszCheck ){
                    LoadString(ghModule, IDS_RENMEMSG4, lpBuffer, sizeof(lpBuffer));
                    LoadString(ghModule, IDS_RENMEMSG5, lpBuffer2, sizeof(lpBuffer2));
                    MessageBox(lpCInfo->hwnd,
                               lpBuffer,
                               lpBuffer2, MB_OK );
                    return(1);
                }

                //
                // if strings are identical, leave.
                //
                if( !lstrcmpi(szName, szDest) ){
                    EndDialog(hDlg, wParam);
                    return(1);
                }

                if( !MoveFile( szName, szDest) )
                    if( !HandleIOError(lpCInfo->hwnd,
                                       (DWORD)MM_RENAME, szName, szDest) ){
                        EndDialog(hDlg, wParam);
                        return(1);
                    }

                if( !PostMessage(lpCInfo->hwnd, WM_COMMAND, MM_FILLFILE,
                                 (LPARAM)0) ) {
                    LoadString(ghModule, IDS_RENMEMSG7, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                }
                EndDialog(hDlg, wParam);
                return(1);
            }
            case SB_CANCEL:{
                EndDialog(hDlg, wParam);
                return(1);
            }
        }
        return(1);
      } // WM_COMMAND
    }

    return(0);
}

/***************************************************************************\
* MkDirProc()
*
* File I/O Make Directory Dialog Box
*
* History:
* 6/5/92  2am
*   Created.
\***************************************************************************/
LRESULT WINAPI MkDirProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    TCHAR   lpBuffer[128];

    switch (message){
      case WM_INITDIALOG:{
        LPCINFO lpCInfo;
        TCHAR   szDest[DIRECTORY_STRING_SIZE];
        LPTSTR  lpszHold;

        lpCInfo = (LPCINFO)lParam;

        //
        // Check to see if there is a terminating backslash, by decrementing
        //  pointer, checking, adding a '\' if necessary.
        //
        lstrcpy(szDest, lpCInfo->CaptionBarText);
        lpszHold = TStrChr(szDest, TEXT('\0'));

        lpszHold--;
        if( *lpszHold != TEXT('\\') ){
            lpszHold++;
            *lpszHold = TEXT('\\');
            lpszHold++;
            *lpszHold = TEXT('\0');
        }

        SendDlgItemMessage( hDlg, SB_DEST, EM_LIMITTEXT,
                            (WPARAM)DIRECTORY_STRING_SIZE, (LPARAM)0);

        //
        // Place name in directory name edit control.
        //
        if( SendDlgItemMessage( hDlg, SB_DEST, WM_SETTEXT, 0,
                                (LPARAM)szDest) == LB_ERR ){
            LoadString(ghModule, IDS_MKDIRMSG, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(1);
        }

        SendDlgItemMessage( hDlg, SB_DEST, EM_SETSEL, (WPARAM)-1, (LPARAM)0);

        return(1);
      }
      case WM_COMMAND:{
        switch(wParam){
            case SB_OK:{
                TCHAR    szDest[DIRECTORY_STRING_SIZE];

                if( SendDlgItemMessage( hDlg, SB_DEST, WM_GETTEXT,
                                        DIRECTORY_STRING_SIZE,
                                        (LPARAM)szDest) == LB_ERR ){
                    LoadString(ghModule, IDS_MKDIRMSG2, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    EndDialog(hDlg, wParam);
                    return(1);
                }

                //
                // If loacation of subdir is invalid, leave.
                //
                if( !CreateDirectory(szDest, NULL) ){
                    LoadString(ghModule, IDS_MKDIRMSG3, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    EndDialog(hDlg, wParam);
                    return(1);
                }

                SendMessage( ghwndDrv1, WM_COMMAND, MM_REFRESH, (LPARAM)0);
                SendMessage( ghwndDrv2, WM_COMMAND, MM_REFRESH, (LPARAM)0);
            }
            case SB_CANCEL:{
                EndDialog(hDlg, wParam);
                return(1);
            }
        }
        return(1);
      }
    }

    return(0);
}

/***************************************************************************\
*
* TextWndProc()
*
* Text Window procedure for displaying miscellaneous messages to user.
*
* History:
* 5/25/92
*   Created.
*
\***************************************************************************/

LRESULT WINAPI TextWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    TCHAR   lpBuffer[128];

    switch (message)
    {
    case WM_CREATE:
        {
        HDC        hDC;
        HGDIOBJ    hOldFont;
        TEXTMETRIC tm;
        LONG       lHeight;

        hDC = GetDC(hwnd);

        hOldFont = SelectObject(hDC, ghFont);
        GetTextMetrics(hDC, &tm);

        //
        // base the height of the window on size of text
        //
        lHeight = tm.tmHeight + GetSystemMetrics(SM_CYBORDER) + 6;

        //
        // saved the height for later reference
        //
        SetWindowLong(hwnd, GWL_USERDATA, lHeight);

            if(hOldFont)
                SelectObject(hDC, hOldFont);

            ReleaseDC(hwnd, hDC);
            break;
        }

    case WM_SETTEXT:
            DefWindowProc(hwnd, message, wParam, lParam);
            if( !InvalidateRect(hwnd, NULL, TRUE) ){
                LoadString(ghModule, IDS_TXTMSG, lpBuffer, sizeof(lpBuffer));
                ErrorMsg(lpBuffer);
                return(1);
            }
            UpdateWindow(hwnd);
            return(1);

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            RECT        rc;
            TCHAR       ach[128];
            int         len, nxBorder, nyBorder;
            HFONT       hOldFont = NULL;
            HBRUSH      hBrush;

            BeginPaint(hwnd, &ps);

            GetClientRect(hwnd,&rc);

            len = GetWindowText(hwnd, ach, sizeof(ach));

            SetBkMode(ps.hdc, TRANSPARENT);

            if( GetParent(hwnd) == ghActiveChild ){
                hBrush = CreateSolidBrush( GetSysColor(COLOR_ACTIVECAPTION) );
                SetTextColor( ps.hdc, GetSysColor(COLOR_CAPTIONTEXT) );
            }
            else{
                hBrush = CreateSolidBrush( GetSysColor(COLOR_INACTIVECAPTION) );
                SetTextColor( ps.hdc, GetSysColor(COLOR_INACTIVECAPTIONTEXT) );
            }

            hOldFont = SelectObject(ps.hdc, ghFont);

            FillRect(ps.hdc, &rc, hBrush);

            nxBorder = GetSystemMetrics(SM_CXBORDER);
            rc.left  += 9*nxBorder;
            rc.right -= 9*nxBorder;

            nyBorder = GetSystemMetrics(SM_CYBORDER);
            rc.top    += 3*nyBorder;
            rc.bottom -= 3*nyBorder;

            ExtTextOut(ps.hdc, rc.left+2*nxBorder, rc.top, ETO_CLIPPED,
                    &rc, ach, len, NULL);

            SetBkMode(ps.hdc, OPAQUE);

            if (hOldFont)
                SelectObject(ps.hdc, hOldFont);

            DeleteObject(hBrush);

            EndPaint(hwnd, &ps);
            return(1);
        }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

/***************************************************************************\
*
* TStrChr()
*
* A strchr() function which is ASCII or UNICODE.
*
* History:
* 2/25/93
*   Created.
*
\***************************************************************************/
PTCHAR TStrChr(const PTCHAR string, UINT c)
{
#ifdef UNICODE
    return wcschr((wchar_t*)string, (wchar_t)c);
#else
    PTCHAR  psz;
    for (psz = string; *psz; psz = CharNext(psz)) {
        if (*psz == (TCHAR)c) {
            break;
        }
    }
    return psz;
#endif
}


DRVPROC.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//
// Forward declarations in DRVPROC.C
//
LONG    GetLBText           (HWND, PTCHAR);

BOOL    OpenListBoxItem     (LPCINFO);
BOOL    RunListBoxItem      (LPCINFO);
BOOL    FilerGetVersion     (LPTSTR, DWORD, LPTSTR);
BOOL    IsDirectory         (LPTSTR, LPTSTR);

BOOL    ExecuteFileAction   (LPSINFO);
BOOL    FillSelectDlg       (HWND, LPSINFO);
BOOL    DoFileIO            (HWND, LPSINFO);
void    UpdateFileLB        (HWND);
BOOL    HandleIOError       (HWND, DWORD, LPTSTR, LPTSTR);
PTCHAR  TStrChr             (const PTCHAR, UINT);

LRESULT WINAPI DrvWndProc   (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI VersionProc  (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI SelectProc   (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI RenameProc   (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI MkDirProc    (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI TextWndProc  (HWND, UINT, WPARAM, LPARAM);


ENUMDRV.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/****************************************************************************
*
*    PROGRAM: Enumdrv.C
*
*    PURPOSE: Determines all drives in the system, both local and remote,
*             and their file system type
*
****************************************************************************/
#define  STRICT
#include <windows.h>
#include "globals.h"
#include "filer.h"
#include "enumdrv.h"

extern HANDLE           ghHeap;  // Handle to global heap, declared in FILER.C
extern CRITICAL_SECTION gHeapCS;  // Global heap critical section var.
extern CRITICAL_SECTION gDrvCS;  // Drive list critical section var.
extern LPDINFO          glpDrives;
extern HANDLEghModule;  // Global Instance handle, declared in FILER.C

/****************************************************************************
*
*    FUNCTION: CheckRM(LPTSTR)
*
*    PURPOSE: Verifies that a removeable media drive contains a disk
*
*    COMMENTS:
*
*        This function is called each time a drive type is determined to be
*        removeable (DRIVE_REMOVEABLE).  An attempt is made to open a
*        file in the root directory of the drive.  If the attempt succeeds,
*        then media is present in the drive and subsequent calls to the
*        drive can be safely made.  If the call fails, then there is no media
*        present in the drive and no attempts should be made to access this
*        drive.  The Error Mode is temporarily set to 1 to allow failures
*        to immediately return to the calling program.  This eliminates
*        unwanted dialog boxes that prompt for disks to be placed in the
*        drive.
*
*    INPUT: szDriveName - removeable media drive name (ex - "a:")
*
*    OUTPUT: Returns TRUE if media present
*                    FALSE if media is not present
*
****************************************************************************/

BOOL CheckRM( LPTSTR lpszDriveName )
{
    TCHAR    szFileName[DIRECTORY_STRING_SIZE];
    DWORD    dwHold;

    SetErrorMode( SEM_FAILCRITICALERRORS );

    lstrcpy( szFileName, lpszDriveName );
    lstrcat( szFileName, TEXT(".") );

    dwHold = GetFileAttributes( szFileName );

    SetErrorMode( 0 );

    //  If no error, media must be in drive.
    if( dwHold != 0xFFFFFFFF ){
        return(TRUE);
    }
    else{
        dwHold = GetLastError();
        if( dwHold != ERROR_NOT_READY )
            ErrorMsg(TEXT("CheckRM: Get Removable Media Info Failure."));

        return(FALSE);
    }

}

/****************************************************************************
*
*    FUNCTION: DWORD EnumDrives(*LPDINFO)
*
*    PURPOSE: Enumerates available drives, and information on them. If the
*               DINFO structure passed in is NULL, it creates a linked list
*               of DINFO structures, reporting information on each available
*               drive.  If DINFO points to an existing structure, the list
*               is updated.
*
*    COMMENTS:
*        The number of available drives is first determined by a call to
*        GetLogicalDrives.  This provides a bit mask of all logical drives.
*
*        For each logical drive, a call is made to GetDriveType to determine
*        the drive type.  If the logical drive is not in the bit mask that
*        was created with the GetLogicalDrives call, then this drive is
*        bypassed.
*
*        GetVolumeInformation is used to determine the file syste for the
*        logical drive. If the drive type is removable, a check must be made
*        to see if the drive contains a disk. If the remote drive contains a
*        disk, then it is safe to proceed with the GetVolumeInformation call.
*
*    INPUT:  LPDINFO dINfo: A pointer to a DRVINFO Structure.
*
*    OUTPUT: Returns the number of DINFO structures in the linked
*            list pointed to by dInfo.  Value is negative if error.
*
\****************************************************************************/

void EnumDrives(LPDINFO *lplpRoot)
{
  DWORD         dwDriveMask;
  DWORD         dwCount;
  LPTSTR        lpszRootPathName=TEXT("?:\\");
  TCHARlpBuffer[128];

  static LPTSTR lpDriveStrings = NULL;
  LPTSTR        lpParse;

  LPDINFO       lpDInfo,                // new node ptr
                lpHold,                 // list walking ptrs
                lpBack,
                lpRoot = *lplpRoot;     // root ptr

  EnterCriticalSection(&gDrvCS);

  //
  // Free old drive strings;
  // With a buffer size of 0, the first call returns the size of buffer needed.
  //
  if(lpDriveStrings != NULL){
    EnterCriticalSection(&gHeapCS);
    HeapFree( ghHeap, 0, lpDriveStrings);
    LeaveCriticalSection(&gHeapCS);
  }

  dwCount=GetLogicalDriveStrings( 0, lpDriveStrings);
  if( !dwCount ){
LoadString(ghModule, IDS_ENUMDRVERR, lpBuffer, sizeof(lpBuffer));
    LeaveCriticalSection(&gDrvCS);
    ErrorMsg(lpBuffer);
    ExitThread((DWORD)-1);
  }

  EnterCriticalSection(&gHeapCS);
  // allocate memory, +1 for trailing NULL
  lpDriveStrings = (LPTSTR)HeapAlloc( ghHeap, 0, (dwCount + 1) * sizeof(TCHAR) );
  LeaveCriticalSection(&gHeapCS);

  if( lpDriveStrings == NULL){
      LeaveCriticalSection(&gDrvCS);
  LoadString(ghModule, IDS_ENUMALOCERR, lpBuffer, sizeof(lpBuffer));
      ErrorMsg(lpBuffer);
      ExitThread((DWORD)-2);
  }

  if(dwCount < GetLogicalDriveStrings( dwCount, lpDriveStrings) ){
      LeaveCriticalSection(&gDrvCS);
  LoadString(ghModule, IDS_ENUMSTRERR, lpBuffer, sizeof(lpBuffer));
      ErrorMsg(lpBuffer);
      ExitThread((DWORD)-3);
  }
  lpParse = lpDriveStrings;

  dwDriveMask=GetLogicalDrives();

  //
  // walk list, inserting, deleting, & updating nodes as necessary
  //
  dwCount = 0;
  lpHold = lpBack = lpRoot;

  for (*lpszRootPathName=TEXT('a');*lpszRootPathName<=TEXT('z');(*lpszRootPathName)++){
    if (dwDriveMask & 1){   // drive exists. Insert or update.

      dwCount++;

      //
      // if lpHold (the list walking ptr) is NULL, or the drive that exists
      //  does not already have a node in the list, allocate a node.
      //
      if( !lpHold || lpHold->DriveLetter > *lpszRootPathName ){
        //
        // Allocating memory for DRVINFO node
        //
        EnterCriticalSection(&gHeapCS);
        lpDInfo = (LPDINFO) HeapAlloc( ghHeap, 0, (DWORD)sizeof(DRVINFO) );
        LeaveCriticalSection(&gHeapCS);
        if( lpDInfo == NULL){
          LeaveCriticalSection(&gDrvCS);
  LoadString(ghModule, IDS_DRVALOCERR, lpBuffer, sizeof(lpBuffer));
          ErrorMsg(lpBuffer);
          ExitThread((DWORD)-4);
        }

        //
        // insert new node into list
        //
        if( lpHold == lpRoot ){
          lpDInfo->next = lpRoot;
          lpRoot = lpDInfo;
        }
        else{
          lpDInfo->next = lpBack->next;
          lpBack->next = lpDInfo;
        }

        lpBack = lpDInfo;
        lpHold = lpDInfo->next;

        lpDInfo->DriveLetter = *lpszRootPathName;
      }
      else{
        if( lpBack != lpHold )
          lpBack = lpBack->next;
        lpHold = lpHold->next;
      }

      lpBack->DriveType = GetDriveType(lpszRootPathName);
      if( lpBack->DriveType == DRIVE_REMOVABLE ||
          lpBack->DriveType == DRIVE_CDROM )
        if( CheckRM(lpszRootPathName ) )
          if( !GetVolumeInformation(lpszRootPathName,NULL,0,NULL,NULL,NULL,
                                   (LPTSTR)lpBack->FileSystem,
                                   (DWORD)FILE_SYSTEM_STRING_SIZE) )
            lstrcpy(lpBack->FileSystem, TEXT("UNKNOWN"));
          else;
        else      // no removable media
          lstrcpy(lpBack->FileSystem, TEXT("UNKNOWN"));
      else        // not removable
        if( !GetVolumeInformation(lpszRootPathName,NULL,0,NULL,NULL,NULL,
                                  (LPTSTR)lpBack->FileSystem,
                                  (DWORD)FILE_SYSTEM_STRING_SIZE) )
          lstrcpy(lpBack->FileSystem, TEXT("UNKNOWN"));
        else;

      lpBack->DriveName = lpParse;
      lpParse += lstrlen(lpParse)+1;
    }
    else{               // drive does not exist.
      if( lpHold )      //   if node exists, delete it.
        if( lpHold->DriveLetter == *lpszRootPathName ){
          if( lpHold == lpRoot )
            lpRoot = lpBack = lpHold->next;
          else
            lpBack->next = lpHold->next;

          EnterCriticalSection(&gHeapCS);
          HeapFree(ghHeap, 0, (LPVOID)lpHold);
          LeaveCriticalSection(&gHeapCS);

          lpHold = lpBack->next;
        }
    }
    dwDriveMask >>= 1;

  } // end for

  *lplpRoot = lpRoot;

  LeaveCriticalSection(&gDrvCS);

  ExitThread(dwCount);
}


ENUMDRV.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/* procedure definitions */

BOOL   CheckRM      (LPTSTR);
BOOL   FillRDStruct (LPDINFO);
void   EnumDrives   (LPDINFO*);


EXPDIR.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************\
*
*    PROGRAM: EXPDIR.C
*
*    PURPOSE: Directory Listbox expansion/collapse functions
*
\***************************************************************************/
#define  STRICT
#include <windows.h>
#include <limits.h>
#include "globals.h"
#include "filer.h"
#include "drvproc.h"
#include "expdir.h"

extern HANDLE ghModule;

/***************************************************************************\
*
* ExpDir()
*
* Thread Function
* Called when directory is selected (Enter or Double-click).  Expands subdirs
*  of selected directory if not expanded, indenting subdirs with a '|'.  If
*  already expaneded, deleteds all subdirs from listbox
*
* Returns:  TRUE if successful, FALSE if error.
*
* History:
* 5/11/93
*   Created.
*
\***************************************************************************/
BOOL ExpDir(LPCINFO lpCInfo)
{
    HANDLE          hFile = NULL;   //  Find file handle
    WIN32_FIND_DATA FileData;       //  Find file info structure

    TCHAR           szFileName[DIRECTORY_STRING_SIZE << 1];   // file name buffer
    PTCHAR          lpszHold;      //  Points to actual name in LB string

    BOOL            fDone = FALSE;  //  Loop flag for finding files in dir

    DWORD           dwAttrib;       //  Holds file attribute flags

    LONG            lIndex,         //  Index of selected listbox member
                    lDirDepth;

    if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
            == WAIT_TIMEOUT ){
        ErrorMsg(TEXT("ExpDir: Dir LB Mutex Timeout."));
        return(0);
    }

    //
    // If the LB is empty, we insert the root, unexpanded
    //
    if( !SendMessage( lpCInfo->hDirLB, LB_GETCOUNT, 0, 0) ){

        TCHAR   szHold[DIRECTORY_STRING_SIZE << 1];

        szHold[0] = TEXT('+');
        lstrcpy( &(szHold[1]), lpCInfo->lpDriveInfo->DriveName );

        //
        // Also copy the DriveName to the Caption Bar.
        //
        lstrcpy(lpCInfo->CaptionBarText, lpCInfo->lpDriveInfo->DriveName );

        SendMessage(lpCInfo->hDirLB, LB_ADDSTRING, 0,
                    (LPARAM)szHold );

        ReleaseMutex( lpCInfo->hDirMutex );
        return(1);
    }

    //
    // Retrieve index of selected (careted) directory.
    //
    lIndex = SendMessage( lpCInfo->hDirLB, LB_GETCARETINDEX,
                          (WPARAM)NULL, (LPARAM)NULL );

    if( SendMessage( lpCInfo->hDirLB, LB_GETTEXT, (WPARAM)lIndex,
                        (LPARAM)szFileName ) < 0 ){
        ErrorMsg(TEXT("Expand Directory:  Get listbox text failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    lDirDepth = GetDirDepth( szFileName, &lpszHold );

    if( lDirDepth == -1 ){
        ErrorMsg(TEXT(" Expand Directory:  GetDirDepth failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    //
    // If Directory is already expanded, collapse it, and vice versa.
    //  First, change the symbol.
    //
    if( *lpszHold == TEXT('-'))
        *lpszHold = TEXT('+');
    else
        *lpszHold = TEXT('-');

    //
    // Clear WM_SETREDRAW flag, so changes will not be seen until entire
    //  expansion/collapse is complete.  Reset flag at end of function
    //
    if( SendMessage(lpCInfo->hDirLB, WM_SETREDRAW, (WPARAM)FALSE,
                (LPARAM)0 ) < 0){
        ErrorMsg(TEXT("Expand Directory:  Clear redraw flag failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    //
    // Delete old dir string, insert new, and reset the selection
    //
    if( SendMessage(lpCInfo->hDirLB, LB_DELETESTRING, (WPARAM)lIndex,
                (LPARAM)0 ) < 0){
        ErrorMsg(TEXT("Expand Directory:  Delete dir string failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }
    if( SendMessage(lpCInfo->hDirLB, LB_INSERTSTRING, (WPARAM)lIndex,
               (LPARAM)szFileName ) < 0){
        ErrorMsg(TEXT("Expand Directory:  Insert dir string failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }
    if( SendMessage(lpCInfo->hDirLB, LB_SETCURSEL, (WPARAM)lIndex,
               (LPARAM)0 ) < 0){
        ErrorMsg(TEXT("Expand Directory:  Insert dir string failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    //
    // The symbol has been changed, now collapse if needed, then reset
    //  redraw flag, leave critical section, and exit.
    //
    if( *lpszHold == TEXT('+') ){
        CollapseDir( lpCInfo, lIndex, lDirDepth);
        ExpDirExit(lpCInfo, hFile);
        return(1);
    }

    //
    // If we're here, directory needs to be expanded.
    //  Enumerate subdirectories beneath the dir entry in the listbox.
    //
    // First, Get the full path of the directory (in szFileName)
    //
    if( !ConstructDirName(lpCInfo, lIndex, szFileName) ){
        ErrorMsg(TEXT("Expand Directory:  ConstructDirName failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    //
    // Check to see if there is a terminating backslash
    //  Then append a '*' as a wildcard for FindFirstFile.
    //
    lpszHold = TStrChr(szFileName, TEXT('\0'));

    lpszHold--;
    if( *lpszHold != TEXT('\\') ){
        lpszHold++;
        *lpszHold = TEXT('\\');
    }

    lpszHold++;
    lstrcpy( lpszHold, TEXT("*"));

    //
    // Start a search on all the files within the directory
    //
    hFile = FindFirstFile( szFileName, &FileData );
    if( hFile == INVALID_HANDLE_VALUE ){
        ErrorMsg(TEXT("Expand Directory: FindFirstFile failure."));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    //
    // Walk all the files in the directory.
    //
    while( !fDone ){

        //
        // Check to see if the thread has been requested to kill itself.
        //  This code does not clear the suicide flag for synchronization
        //  reasons - it is left to the calling code.
        //
        if( lpCInfo->fSuicide ){
            ErrorMsg(TEXT("Expand Directory: killing thread per request."));
            ExpDirExit(lpCInfo, hFile);

            //
            // Post an MM_REFRESH Message if the user has re-collapsed the
            //  Dir LB.
            //
            if( !lpCInfo->fDirExpand )
                if( !PostMessage(lpCInfo->hwnd, WM_COMMAND, MM_REFRESH,
                                 (LPARAM)0) )
                    ErrorMsg(TEXT("ExpDir: MM_REFRESH call failure."));

            //
            // We must return failure (0) here, so if FullExpand is calling,
            //  it will terminate.
            //
            return(0);
        }

        //
        // Append filename to path, and get attributes
        //
        lstrcpy(lpszHold, FileData.cFileName);
        dwAttrib = GetFileAttributes( szFileName );

        //
        // Check if file is a directory.  If not, or if '.' or '..', fall
        //  through.  If so, add it to the directory listbox.
        //
        if( dwAttrib & FILE_ATTRIBUTE_DIRECTORY )
            if( lstrcmp( FileData.cFileName,TEXT(".") ) )
                if( lstrcmp( FileData.cFileName,TEXT("..") ) ){
                    TCHAR   szLBEntry[DIRECTORY_STRING_SIZE << 1];

                    ConstructLBEntry(lDirDepth, FileData.cFileName, szLBEntry);
                    //
                    //  Increment index in order to add subdir after
                    //  dirs just inserted.
                    //
                    if( SendMessage(lpCInfo->hDirLB, LB_INSERTSTRING,
                                       (WPARAM)++lIndex,
                                       (LPARAM)szLBEntry) < 0){
                        ErrorMsg(TEXT("Expand Directory: error inserting string."));
                        ExpDirExit(lpCInfo, hFile);
                        return(0);
                    }
                }

        fDone = !FindNextFile( hFile, &FileData );
    }

    ExpDirExit(lpCInfo, hFile);
    return(1);
}

/***************************************************************************\
*
* ExpDirExit()
*
* Performs clean-up operations for ExpDir, closing handles, etc.
*
* History:
* 5/28/93
*   Created.
*
\***************************************************************************/
void ExpDirExit(LPCINFO lpCInfo, HANDLE hFile)
{

    //
    // Reset redraw flag. Post this message, to avoid synchro problems
    //
    if( PostMessage(lpCInfo->hDirLB, WM_SETREDRAW, (WPARAM)TRUE,
                (LPARAM)0 ) < 0)
        ErrorMsg(TEXT("Expand Directory:  Clear redraw flag failure"));

    //
    // Close FindFirstFile session
    //
    if( hFile != NULL )
        FindClose(hFile);

    //
    // Release Dir LB Mutex
    //
    ReleaseMutex( lpCInfo->hDirMutex);
}

/***************************************************************************\
*
* ConstructDirName()
*
* Builds the fully qualified path of the current directory, by walking back
*  through the Dir LB tree.
*
* Returns:  TRUE if successful, FALSE if error.
*           Returns the full directory path of given directory name in
*             lpszDirName.
*
* History:
* 5/11/93
*   Created.
*
\***************************************************************************/
BOOL ConstructDirName(LPCINFO lpCInfo, LONG lIndex, LPTSTR lpszDirName)
{
    LONG    lDirDepth,      //  Depth of selected directory
            lSeekDepth = LONG_MAX;

    TCHAR   szFileName[DIRECTORY_STRING_SIZE << 1];   // file buffer
    LPTSTR  lpszInfoPtr,
            lpszHold;

    //
    // Clear the directory name buffer
    //
    *lpszDirName = TEXT('\0');

    //
    // Walk up the entries in the listbox, constructing the full path from
    //   the bottom up.
    //
    while( lIndex >= 0 ){

        //
        // Get listbox text, and compute the depth of the directory
        //
        if( SendMessage( lpCInfo->hDirLB, LB_GETTEXT, (WPARAM)lIndex,
                            (LPARAM)szFileName ) < 0 ){
            ErrorMsg(TEXT(" Expand Directory:  Get listbox text failure"));
            return(0);
        }

        lDirDepth = GetDirDepth(szFileName, &lpszInfoPtr);
        if( lDirDepth == -1){
            ErrorMsg(TEXT("ConstructDirName: GetDirDepth failed"));
            return(0);
        }

        //
        // If we've reached the next level up, add to the directory name
        //
        if( lDirDepth < lSeekDepth ){
            lSeekDepth = lDirDepth;

            // check if we will exceed the size of our buffer
            if( lstrlen(lpszInfoPtr) + lstrlen(lpszDirName) >
                    (DIRECTORY_STRING_SIZE << 1) ){
                ErrorMsg(TEXT("ConstructDirName:  Exceeded Directory Size limit"));
                return(0);
            }

            // Find the end of the directory name
            lpszHold = TStrChr(lpszInfoPtr, TEXT('\0'));

            // If we're not at the root, add a '\'
            if( lIndex && (*lpszDirName != TEXT('\0')) )
                *lpszHold++ = TEXT('\\');

            // Append the heretofore computed path to the end of the dir name
            lstrcpy( lpszHold, lpszDirName);
            // Copy the whole path so far back into the final buffer
            lstrcpy( lpszDirName, ++lpszInfoPtr);
        }

        // If the first level dir has been added, jump to the root,
        //   else go up to the previous entry in the listbox.
        if( lDirDepth == 1 )
            lIndex = 0;
        else
            lIndex--;
    }

    return(TRUE);
}

/***************************************************************************\
*
* GetDirDepth()
*
* Returns:  -1 if error
*           otherwise, the depth of the directory (i.e. root is depth 0,
*           c:\foo is depth 1, etc.  This is computed in perhaps not the
*           most efficient way, by counting the '|'s proceeding the name.
*
*           This function also returns in lpszDirName a pointer to the
*           end of the preceeding '|' characters within the given listbox str.
*           If the function fails, this pointer value is undefined.
*
* The left shifted index is to skip the tab characters. See ConstructLBEntry().
*
* History:
* 5/7/93
*   Created.
*
\***************************************************************************/
LONG GetDirDepth(LPTSTR lpszLBString, LPTSTR *lpszDirName)
{
    TCHAR cBar;
    LONG  lCount = 0;

    do{

        cBar = lpszLBString[lCount << 1];

        if( cBar == TEXT('|') )
            lCount++;
        else
            if( cBar != TEXT('+') && cBar != TEXT('-') ){
                ErrorMsg(TEXT("GetDirDepth: string parse error"));
                return(-1);
            }

    }while( cBar != TEXT('+') && cBar != TEXT('-') );

    *lpszDirName = &(lpszLBString[lCount << 1]);

    return( lCount );
}

/***************************************************************************\
*
* CollapseDir()
*
* If directory is expanded, collapses it, by deleteing any subdirectory
*  entries below it.
*
* Returns:  TRUE if successful, FALSE if error
*
* History:
* 5/11/93
*   Created.
*
\***************************************************************************/
BOOL CollapseDir(LPCINFO lpCInfo, LONG lIndex, LONG lDirDepth)
{
    TCHAR   szFileName[DIRECTORY_STRING_SIZE << 1];   // file name buffer
    LPTSTR  lpszNamePtr;

    LONG    lDepthHold;

    //
    // Remove any following LB entries until we return to same depth
    //
    do{

        if( SendMessage( lpCInfo->hDirLB, LB_GETTEXT, (WPARAM)lIndex + 1,
                            (LPARAM)szFileName ) < 0 ){
            ErrorMsg(TEXT(" Expand Directory:  Get listbox text failure"));
            return(0);
        }

        lDepthHold = GetDirDepth( szFileName, &lpszNamePtr);
        if( lDirDepth == -1 ){
            ErrorMsg(TEXT(" Expand Directory:  GetDirDepth failure"));
            return(0);
        }

        if( lDirDepth < lDepthHold )
            if( SendMessage( lpCInfo->hDirLB, LB_DELETESTRING, (WPARAM)lIndex + 1,
                                (LPARAM)0 ) < 0 ){
                ErrorMsg(TEXT(" Expand Directory:  Delete String failure"));
                return(0);
            }

    }while( lDirDepth < lDepthHold );

    return(1);
}

/***************************************************************************\
*
* ConstructLBEntry()
*
* Given the parent's directory depth, and the subdirectory name, inserts
* '|'s equal to the depth+1 of the parent (plus tab character), and an
* unexpanded '+' directory marker, then the name of the subdirectory.
*
* The left shifted index is to skip the tab characters.
*
* Returns:  void.  the completed listbox entry is returned in szLBEntry.
*
* History:
* 5/14/93
*   Created.
*
\***************************************************************************/
void ConstructLBEntry(LONG lDirDepth, LPTSTR szFileName, LPTSTR szLBEntry)
{
    int i;

    for(i = 0; i <= lDirDepth; i++){
        szLBEntry[i << 1] = TEXT('|');
        szLBEntry[(i << 1) + 1] = 9;
    }

    szLBEntry[i << 1] = TEXT('+');
    szLBEntry[(i << 1) + 1] = TEXT('\0');

    lstrcat(szLBEntry, szFileName);
}

/***************************************************************************\
*
* FullExpand()
*
* From the unexpanded root, walks down directory LB, expanding each directory
*  until it reaches the end of the tree.
*
* Returns:  void
*
* History:
* 5/20/93
*   Created.
*
\***************************************************************************/
BOOL FullExpand(LPCINFO lpCInfo)
{
    LONG lIndex = 0;

    if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
            == WAIT_TIMEOUT ){
        ErrorMsg(TEXT("FullExpand: Dir LB Mutex Timeout."));
        return(0);
    }

    while( SendMessage(lpCInfo->hDirLB, LB_SETCURSEL,
                        (WPARAM)lIndex,
                        (LPARAM)0) != LB_ERR ){
        if( !ExpDir( lpCInfo ) ){
            ErrorMsg(TEXT("Full Expand: ExpDir failure."));
            ReleaseMutex( lpCInfo->hDirMutex );

            // This is in case the ExpDir failed because a change drive
            //  command caused a kill.
            if( !PostMessage(lpCInfo->hwnd, WM_COMMAND, MM_REFRESH,
                             (LPARAM)0) )
                ErrorMsg(TEXT("ExpDir: MM_REFRESH call failure."));
            return(0);
        }

        lIndex++;
    }

    //  Set selection in listboxes to first item.
    if( SendMessage(lpCInfo->hDirLB, LB_SETCURSEL,
                        (WPARAM)0,
                        (LPARAM)0) == LB_ERR ){
        ErrorMsg(TEXT("Full Expand: Dir LB Set Selection Error"));
        ReleaseMutex( lpCInfo->hDirMutex );
        return(0);
    }

    ReleaseMutex( lpCInfo->hDirMutex );
    return(1);
}


EXPDIR.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

BOOL ExpDir(LPCINFO);
void ExpDirExit(LPCINFO, HANDLE);
BOOL ConstructDirName(LPCINFO, LONG, LPTSTR);
LONG GetDirDepth(LPTSTR, LPTSTR*);
BOOL CollapseDir(LPCINFO, LONG, LONG);
void ConstructLBEntry(LONG, LPTSTR, LPTSTR);
BOOL FullExpand(LPCINFO);


FILER.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/******************************Module*Header*******************************\
*
* Module Name:  Filer.c
*
*
* Filer: SDK sample
*   +   Simple File Management program with GUI front end.
*       Demonstrates Win32 File I/O API and various User algorithms.
*
* Dependencies:
*
*   (#defines)
*   (#includes)
*         -Enumdrv.h
*         -Walk.h
*         -Drvproc.h
*         -Filer.h
*
\**************************************************************************/
#define  STRICT
#include <windows.h>
#include <stdlib.h>
#include <stdarg.h>
#include "globals.h"
#include "enumdrv.h"
#include "drvproc.h"
#include "filer.h"

//
// Global Handles
//
HANDLE  ghModule;                     // Process Instance handle

HWND    ghwndMain;                       // Main window handle
HWND    ghwndDrives;                     // Drives Toolbar window handle
HWND    ghwndFunction;                   // Function Toolbar window handle
HWND    ghwndCommand;                    // Command Line window handle
HWND    ghwndDrv1,
        ghwndDrv2;
HWND    ghActiveChild = NULL;            // Handle to active drive child window
HWND    ghFocusWnd;                      // Handle to last listbox with focus

HANDLE  ghHeap;                          // Application heap handle
HFONT   ghFont;
HANDLE  ghDrvThread = NULL;

HMENU   ghMenu;                          // App Menu variables

//
//   Global Data Items
//
BOOL                gfDrvWndOrient = SIDE_BY_SIDE, // relative Drv child pos.
                    gfKeepCommandWin = FALSE;

DRVCHILDINFO        gDrvChild1Info,
                    gDrvChild2Info;

LPDINFO             glpDrives = NULL;    // Root of Available Drives linked list

CRITICAL_SECTION    gDrvCS;              // Drive list critical section var.
CRITICAL_SECTION    gHeapCS;             // Global heap critical section.

TCHAR    gszCommandLine[DIRECTORY_STRING_SIZE * 2];
TCHAR    gszExtensions[NUM_EXTENSION_STRINGS][EXTENSION_LENGTH];

VKINFO  gVKArray[NUM_VERSION_INFO_KEYS];     // .EXE version info array.
                                             // See GetVersion() in DRVPROC.C

/***************************************************************************\
* WinMain
*
*
* History:
* 04-17-91
*   Created.
\***************************************************************************/
int WINAPI WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow)
{
    MSG    msg;
    HANDLE hAccel;

    //-- check to make sure we are running on Windows NT
    if( GetVersion() & 0x80000000 ) {            // 0x80000000 == Windows 3.1
        MessageBox( NULL,
        TEXT("Sorry, Filer must run under Windows NT.  Filer will now terminate."),
        TEXT("Error: Windows NT Required"),  MB_OK );
        return( 0 );
    }

    ghModule = hInstance;

    if (!InitializeApp()) {
        ErrorMsg(TEXT("Filer: InitializeApp failure!"));
        return 0;
    }
    ShowWindow(ghwndMain, nCmdShow);

    if (!(hAccel = LoadAccelerators (ghModule, MAKEINTRESOURCE(ACCEL_ID))))
        ErrorMsg(TEXT("Filer: Load Accel failure!"));

    while (GetMessage(&msg, NULL, 0, 0)) {
        if( !TranslateAccelerator(ghwndMain, hAccel, &msg) ) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return 1;

    UNREFERENCED_PARAMETER(lpCmdLine);
    UNREFERENCED_PARAMETER(hPrevInstance);
}

/***************************************************************************\
* InitializeApp
*
* History:
* 5/5/92
*   Created
\***************************************************************************/

BOOL InitializeApp(void)
{
    WNDCLASS wc;

    wc.style            = 0;
    wc.lpfnWndProc      = (WNDPROC)MainWndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra   = 0;
    wc.hInstance        = ghModule;
    wc.hIcon            = LoadIcon(ghModule, MAKEINTRESOURCE(UI_FILERICON));
    wc.hCursor          = LoadCursor(ghModule, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)(COLOR_APPWORKSPACE);
    wc.lpszMenuName     = TEXT("FilerMenu");
    wc.lpszClassName    = TEXT("FilerClass");

    if (!RegisterClass(&wc))
    return(FALSE);

    wc.lpfnWndProc  = DrvWndProc;
    wc.hIcon        = NULL;
    wc.lpszMenuName = NULL;
    wc.lpszClassName    = TEXT("DrvClass");

    if (!RegisterClass(&wc))
        return(FALSE);

    wc.style        = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc  = DriveBarProc;
    wc.hbrBackground    = (HBRUSH)(COLOR_BTNSHADOW);
    wc.lpszClassName    = TEXT("DriveBarClass");

    if (!RegisterClass(&wc))
            return(FALSE);

    wc.style        = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc  = TextWndProc;
    wc.hbrBackground    = (HBRUSH)(COLOR_INACTIVECAPTION);
    wc.lpszClassName    = TEXT("TextClass");

    if (!RegisterClass(&wc))
            return(FALSE);

    ghMenu = LoadMenu(ghModule, TEXT("FilerMenu"));

    ghwndMain = CreateWindow(TEXT("FilerClass"),
                             TEXT("Filer"),
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT,
                             CW_USEDEFAULT,
                             MAIN_WIDTH,
                             MAIN_HEIGHT,
                             HWND_DESKTOP,
                             ghMenu,
                             ghModule,
                             NULL);

    if (ghwndMain == NULL)
    return(FALSE);

    return(TRUE);
}

/***************************************************************************\
* MainWndProc
*
* History:
* 05-01-92  Created.
\***************************************************************************/

LRESULT WINAPI MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {

      case WM_CREATE:{
        LOGFONT    lf;
        HDC        hDC;
        HGDIOBJ    hOldFont;
        TEXTMETRIC tm;

        DWORD dwThreadID;

        //
        // Initialize drive list and Set Directory critical sections.
        //
        InitializeCriticalSection(&gDrvCS);
        InitializeCriticalSection(&gHeapCS);

        ghDrvThread = CreateThread(NULL, 0,
                                   (LPTHREAD_START_ROUTINE)EnumDrives,
                                   (LPVOID)&glpDrives,
                                   0, &dwThreadID);

        //
        // Create the application's heap
        //
        ghHeap = HeapCreate( 0, (DWORD)sizeof(DRVCHILDINFO), 0);
        if( ghHeap == NULL )
            ErrorMsg(TEXT("Main Create: Failed in Creating Heap"));

        //
        // Compute default application font by creating a bold version
        //   of the system default icon font.
        //
        SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf),
                             (PVOID) &lf, FALSE);

        hDC = GetDC(hwnd);

        // this is the height for 8 point size font in pixels.
        //  (1 point = 1/72 in.)
        //  Japanese fonts have different characteristics and don't look good in bold
        if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE)
            lf.lfHeight = 10 * GetDeviceCaps(hDC, LOGPIXELSY) / 72;
        else {
            lf.lfHeight = 8 * GetDeviceCaps(hDC, LOGPIXELSY) / 72;
            lf.lfWeight = BOLD_FONT;
        }

        ghFont = CreateFontIndirect(&lf);
        hOldFont = SelectObject(hDC, ghFont);
        GetTextMetrics(hDC, &tm);

        if(hOldFont)
            SelectObject(hDC, hOldFont);

        ReleaseDC(hwnd, hDC);

        //
        // Create Drive windows
        //
        gDrvChild1Info.hParent = hwnd;
        gDrvChild2Info.hParent = hwnd;

        ghwndDrv1 = CreateWindow(TEXT("DrvClass"), NULL,
                                 WS_CHILD |
                                 WS_CLIPSIBLINGS | WS_VISIBLE,
                                 0, 0, 0, 0,
                                 hwnd, (HMENU) 1, ghModule,
                                 (LPVOID)&gDrvChild1Info);

        ghActiveChild = ghwndDrv1;

        //
        // Set initial focus to Drive Child 1's Directory listbox.
        //
        ghFocusWnd = ((LPCINFO)GetWindowLong(ghwndDrv1, GWL_USERDATA))->hDirLB;

        ghwndDrv2 = CreateWindow(TEXT("DrvClass"), NULL,
                                 WS_CHILD |
                                 WS_CLIPSIBLINGS | WS_VISIBLE,
                                 0, 0, 0, 0,
                                 hwnd, (HMENU) 2, ghModule,
                                 (LPVOID)&gDrvChild2Info);

        //
        // Create DriveBar, FunctionBar and Command windows
        //
        ghwndDrives = CreateWindow(TEXT("DriveBarClass"), NULL,
                                   WS_CHILD | WS_VISIBLE | WS_BORDER,
                                   0, 0, 0, 0,
                                   hwnd, (HMENU) 3, ghModule,
                                   (LPVOID)NULL);

        ghwndFunction = CreateDialog(ghModule,
                                    TEXT("FunctionBar"),
                                    hwnd,
                                    (DLGPROC)FunctionBarProc);

        ghwndCommand = CreateWindow(TEXT("EDIT"), NULL,
                                   ES_AUTOHSCROLL | ES_LEFT | WS_BORDER |
                                   ES_NOHIDESEL | WS_CHILD | WS_VISIBLE,
                                   0, 0, 0, 0,
                                   hwnd,
                                   (HMENU) COMMAND_ID,
                                   ghModule,
                                   NULL);

        //
        // Compute height of command window from font; store in window info.
        // Set command window to default font.
        //
        SetWindowLong( ghwndCommand, GWL_USERDATA,
                       tm.tmHeight + GetSystemMetrics(SM_CYBORDER) + 6);
        SendMessage(ghwndCommand, WM_SETFONT, (WPARAM)ghFont, (LPARAM)FALSE);

        //
        // Load String table entries
        //
        LoadString( ghModule, STR_EXE, &gszExtensions[0][0], EXTENSION_LENGTH);
        LoadString( ghModule, STR_COM, &gszExtensions[1][0], EXTENSION_LENGTH);
        LoadString( ghModule, STR_CMD, &gszExtensions[2][0], EXTENSION_LENGTH);
        LoadString( ghModule, STR_BAT, &gszExtensions[3][0], EXTENSION_LENGTH);

        UpdateDrivesMenu(ghMenu, ghDrvThread);

        return(1);
      }

      case WM_COMMAND:{

        //
        // The menu Identifiers for the drives are (potentially)
        // MM_DRIVE_NUM + 0 thru MM_DRIVE_NUM + 25. They all go to the
        // same case, so we will put the Menu ID in lParam, and
        // MM_DRIVE_NUM in LOWORD(wParam).
        //
        if( (LOWORD(wParam) - MM_DRIVE_NUM) <= 25 &&
            (LOWORD(wParam) - MM_DRIVE_NUM) >= 0 ){
            lParam = LOWORD(wParam);
            wParam = MM_DRIVE_NUM;
        }

        switch (LOWORD(wParam)) {
            //
            // If a drive is selected from the Drives menu, or clicked
            //  on the drives toolbar, the currently active child will
            //  switch to this drive. Message 'unconverted' (see top of
            //  WM_COMMAND case), and sent to DriveBarProc
            //
            case MM_DRIVE_NUM:{

                SendMessage(ghwndDrives, WM_COMMAND,
                            (WPARAM)lParam, (LPARAM)NULL);
                return(1);
            }

            //
            // Passes these WM_COMMAND messages to the appropriate active child
            //  window proc for processing
            //
            case MM_TAB:
            case MM_ESCAPE:
            case MM_OPEN:
            case MM_COPY:
            case MM_DELETE:
            case MM_MOVE:
            case MM_RENAME:
            case MM_MKDIR:
            case MM_EXPAND:
            case MM_VERSION:{

                SendMessage(ghActiveChild, WM_COMMAND, wParam, lParam);
                return(1);
            }

            case MM_EXIT:{
                SendMessage(ghwndMain, WM_CLOSE, wParam, lParam);
                return(1);
            }

            //
            // Creates the drive enumeration thread to re-enumerate the
            //   available drives in the main menu.  Also sends a refresh
            //   to the active drive child, and repaints the window.
            //
            case MM_REFRESH: {
                DWORD   dwThreadID;

                //
                // Initialize/Refresh Drives linked list
                //

                if( WaitForSingleObject(ghDrvThread, 0) != WAIT_TIMEOUT ){

                    //
                    // Close previous Drive Thread handle before creating new handle.
                    //
                    CloseHandle( ghDrvThread );

                    ghDrvThread = CreateThread(NULL, 0,
                                           (LPTHREAD_START_ROUTINE)EnumDrives,
                                           (LPVOID)&glpDrives,
                                           0, &dwThreadID);

                    //
                    // Refresh active child, drive toolbar, and drives menu
                    //
                    SendMessage(ghActiveChild, WM_COMMAND, wParam, lParam);
                    SendMessage(ghwndDrives, WM_COMMAND, wParam, lParam);
                    UpdateDrivesMenu(ghMenu, ghDrvThread);

                    //
                    // Mark all for repaint
                    //
                    InvalidateRect(hwnd,NULL,TRUE);

                }
                else
                    MessageBeep(MB_ICONASTERISK);

                return(1);
            }

            //
            // Swaps the directory and file list boxes of the active drv child.
            //
            case MM_SWAP:{
                LPCINFO lpCInfo;
                RECT    rect;

                lpCInfo = (LPCINFO)GetWindowLong(ghActiveChild, GWL_USERDATA);

                //
                // Switch the flag which indicates which side the Directory
                //  LB is on.  This is used by the WM_SIZE case of DrvWndProc.
                //
                lpCInfo->fDirLeft = !lpCInfo->fDirLeft;

                //
                // Send size message with current size to active child,
                //   in order to redraw the listboxes.
                //
                if( !GetClientRect( ghActiveChild, &rect ) )
                    return(0);

                SendMessage( ghActiveChild, WM_SIZE, SIZENORMAL,
                             MAKELONG( rect.right - rect.left,
                                       rect.bottom - rect.top) );
                return(1);
            }

            case MM_KEEPCMD:{

                gfKeepCommandWin = !gfKeepCommandWin;

                if( gfKeepCommandWin )
                    CheckMenuItem( ghMenu, MM_KEEPCMD,
                                    MF_BYCOMMAND | MF_CHECKED);
                else
                    CheckMenuItem( ghMenu, MM_KEEPCMD,
                                MF_BYCOMMAND | MF_UNCHECKED);
            }
            break;

            //
            // Toggles the relative Drive Child orientaion between
            // Over/under and side/side.  gfDrvWndOrient is a flag checked
            // by WM_SIZE to size Drv children
            //
            case MM_ORIENT:{
                RECT    rect;

                if( gfDrvWndOrient == OVER_UNDER )
                    gfDrvWndOrient = SIDE_BY_SIDE;
                else
                    gfDrvWndOrient = OVER_UNDER;

                //
                // Send size message with current size to self (main window),
                //   in order to redraw the Drv children.
                //
                if( !GetClientRect( hwnd, &rect ) )
                    return(0);

                SendMessage( hwnd, WM_SIZE, SIZENORMAL,
                             MAKELONG( rect.right - rect.left,
                                       rect.bottom - rect.top) );

                InvalidateRect(ghwndDrv1,NULL,TRUE);
                InvalidateRect(ghwndDrv2,NULL,TRUE);

                return(1);
            }

            //
            // Toggles the active drive child.  Sent from menu.
            // This behaves the same as a WM_MOUSEACTIVATE in one of the
            //   Drive children.  The PostMessage is so the current Active
            //   child will not process the MM_TOGGLE message until after it
            //   is no longer active.
            //
            case MM_ACTIVEDRV:{
                LPCINFO lpCInfo;

                PostMessage(ghActiveChild, WM_COMMAND, (WPARAM)MM_TOGGLE,
                            (LPARAM)NULL);

                if( ghActiveChild == ghwndDrv1 )
                    ghActiveChild = ghwndDrv2;
                else
                    ghActiveChild = ghwndDrv1;

                SendMessage(ghActiveChild, WM_COMMAND, (WPARAM)MM_TOGGLE,
                            (LPARAM)NULL);

                // change drive button
                lpCInfo = (LPCINFO)GetWindowLong(ghActiveChild, GWL_USERDATA);
                SendMessage(ghwndDrives, WM_COMMAND, MM_ACTIVEDRV,
                            (LPARAM)lpCInfo->lpDriveInfo);
                return(1);
            }

            //
            // Sent by the Command line Edit control.
            //
            case COMMAND_ID:{

                if( HIWORD(wParam) == EN_SETFOCUS )
                    ghFocusWnd = ghwndCommand;
            }
            break;

            //
            // Launches the About DialogBox.
            //
            case MM_ABOUT:{
                TCHAR   lpBuffer[128];

                if (DialogBox(ghModule, TEXT("AboutBox"), ghwndMain, (DLGPROC)AboutProc) == -1) {
                    LoadString(ghModule, IDS_ABOUTDLGERR, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                }
                return(1);
            }

            default:
                return( DefWindowProc(hwnd, message, wParam, lParam) );
        }
        return(1);
      }
      //
      // Whenever the window is resized, its children have to be
      //  resized accordingly.  The GetWindowLong values are the height
      //  of the windows queried by this function, and are set in the
      //  WM_CREATE cases of their respective WNDPROCs.
      //
      case WM_SIZE:{
        int DrvWndHeight;

        //
        // Always put the command window at the bottom of the frame window
        //
        MoveWindow(ghwndCommand,
           0,
           HIWORD(lParam) - GetWindowLong(ghwndCommand, GWL_USERDATA),
           LOWORD(lParam),
           GetWindowLong(ghwndCommand, GWL_USERDATA),
               TRUE);
        //
        // Always put the drives toolbar at the top of the frame window
        //
        MoveWindow(ghwndDrives,
           0,
           0,
           LOWORD(lParam),
           GetWindowLong(ghwndDrives, GWL_USERDATA),
               TRUE);

        //
        // Always put the Function window just below the drives toolbar.
        //
        MoveWindow(ghwndFunction,
                   0,
                   GetWindowLong(ghwndDrives, GWL_USERDATA),
                   LOWORD(lParam),
                   GetWindowLong(ghwndFunction, GWL_USERDATA),
                   TRUE);

        //
        // Always size the Drive Children between the Drives and Command
        // windows. The width is set so that borders overlap.
        //

        if( gfDrvWndOrient == OVER_UNDER ){

            DrvWndHeight = ( HIWORD(lParam) -
                           GetWindowLong(ghwndDrives, GWL_USERDATA) -
                           GetWindowLong(ghwndFunction, GWL_USERDATA) -
                           GetWindowLong(ghwndCommand, GWL_USERDATA) ) / 2;

            MoveWindow(ghwndDrv1,
                        -1,
                        GetWindowLong(ghwndDrives, GWL_USERDATA)+
                            GetWindowLong(ghwndFunction, GWL_USERDATA),
                        LOWORD(lParam) + 2,
                        DrvWndHeight,
                        TRUE);

            MoveWindow(ghwndDrv2,
                        -1,
                        GetWindowLong(ghwndDrives, GWL_USERDATA)+
                            GetWindowLong(ghwndFunction, GWL_USERDATA) +
                            DrvWndHeight,
                        LOWORD(lParam) + 2,
                        DrvWndHeight,
                        TRUE);
        }
        else{

            DrvWndHeight = HIWORD(lParam) -
                           GetWindowLong(ghwndDrives, GWL_USERDATA) -
                           GetWindowLong(ghwndFunction, GWL_USERDATA) -
                           GetWindowLong(ghwndCommand, GWL_USERDATA);

            MoveWindow(ghwndDrv1,
                        -1,
                        GetWindowLong(ghwndDrives, GWL_USERDATA)+
                            GetWindowLong(ghwndFunction, GWL_USERDATA),
                        LOWORD(lParam)/2 + 1,
                        DrvWndHeight,
                        TRUE);

            MoveWindow(ghwndDrv2,
                        LOWORD(lParam)/2,
                        GetWindowLong(ghwndDrives, GWL_USERDATA)+
                            GetWindowLong(ghwndFunction, GWL_USERDATA),
                        LOWORD(lParam)/2 + 1,
                        DrvWndHeight,
                        TRUE);
        }

        return(1);
      }

      case WM_DESTROY: {
        //
        // Close last drive thread handle, the global heap and it's corresponding critical section,
        //  the created font, and the Drive list critical section.
        //
        CloseHandle( ghDrvThread );

        EnterCriticalSection(&gHeapCS);
        HeapDestroy(ghHeap);
        LeaveCriticalSection(&gHeapCS);

        DeleteObject(ghFont);

        DeleteCriticalSection(&gDrvCS);
        DeleteCriticalSection(&gHeapCS);

        PostQuitMessage(0);
        return(1);
      }

      default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
}

/***************************************************************************\
* AboutProc
*
* About dialog proc.
*
* History:
*   05-13-92      Created.
\***************************************************************************/

LRESULT WINAPI AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
      case WM_INITDIALOG:{
        return TRUE;
      }

      case WM_COMMAND:{
        if (wParam == IDOK)
            EndDialog(hDlg, wParam);
        break;
      }
    }

    return FALSE;

    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(hDlg);
}

/***************************************************************************\
*
* DriveBarProc()
*
* Drive Toolbar procedure for displaying available drive Icons.
*  A bitmap button is displayed corresponding to the drive type of the
*  given drive, with the drive letter alongside.
*  ghwndDrives is the global handle assoc. w/ this window procedure.
*
*
* History:
* 6/9/92
*   Created.
*
\***************************************************************************/

LRESULT WINAPI DriveBarProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HBITMAP  hDrvBmp[NUM_BITMAPS];
    static HBRUSH   hBrush;         // background brush
    static int      nDrvEntryWidth; // width of button/letter entry
    static int      yVal;           // y value in toolbar for top left of bmp
    static LPBINFO  lpDrvButtonRoot;
    static int      nActiveDrvIndex;
    TCHAR           lpBuffer[128];

    switch (message)
    {
        case WM_CREATE:{
            HDC        hDC;
            HGDIOBJ    hOldFont;
            TEXTMETRIC tm;
            LONG       lHeight;

            lpDrvButtonRoot = NULL;

            //
            // Load drive button bitmaps.
            //
            for(yVal = 0; yVal < NUM_BITMAPS; yVal++)
                hDrvBmp[yVal] = LoadBitmap( ghModule,
                                      MAKEINTRESOURCE(UB_BMP_MARKER + yVal) );

            //
            // Sets background color of Toolbar non-modal dialog children.
            //
            hBrush = CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));

            hDC = GetDC(hwnd);

            hOldFont = SelectObject(hDC, ghFont);
            GetTextMetrics(hDC, &tm);

            // base the height of the window on size of text
            // Different display height is optimal if on a Japanese language system
            //
            if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE)
                lHeight = tm.tmHeight + GetSystemMetrics(SM_CYBORDER) + 8;
            else
                lHeight = tm.tmHeight + GetSystemMetrics(SM_CYBORDER) + 6;

            //
            // saved the window height, drive button entry width
            //   and button y starting value for later reference
            //
            SetWindowLong(hwnd, GWL_USERDATA, lHeight);

            //
            // Width of one button entry = spacing, button, sm. space,
            //   drive letter, spacing.
            //
            nDrvEntryWidth = DRIVE_BITMAP_SPACING + DRIVE_BITMAP_WIDTH +
                             DRIVE_LETTER_SPACING + tm.tmAveCharWidth +
                             DRIVE_BITMAP_SPACING;

            //
            // Center bitmaps (by height) in drive toolbar.
            //
            yVal = (lHeight - DRIVE_BITMAP_HEIGHT)/2;

            SelectObject(hDC, hOldFont);
            ReleaseDC(hwnd, hDC);

            SendMessage(hwnd, WM_COMMAND, (WPARAM)MM_REFRESH, (LPARAM)NULL);

            break;
        }

        case WM_COMMAND:{
            //
            // The button Identifiers for the drives are (potentially)
            // MM_DRIVE_NUM + 0 thru MM_DRIVE_NUM + 25. They all go to the
            // same case, so we will put the Menu ID in lParam, and
            // MM_DRIVE_NUM in LOWORD(wParam).
            //
            if( (LOWORD(wParam) - MM_DRIVE_NUM) <= 25 &&
                (LOWORD(wParam) - MM_DRIVE_NUM) >= 0 ){
                lParam = LOWORD(wParam);
                wParam = MM_DRIVE_NUM;
            }

            switch( LOWORD(wParam) ){
              case MM_REFRESH:{

                LPDINFO lpWalk;
                LPBINFO lpBWalk, lpBHold;
                LPCINFO lpCInfo;
                int     xVal = 0;
                int     nCount = MM_DRIVE_NUM;
                TCHAR   lpBuffer[128];

                lpCInfo = (LPCINFO)GetWindowLong(ghActiveChild, GWL_USERDATA);

                //
                // Wait for Drive Thread to complete, if necessary.
                //
                WaitForSingleObject(ghDrvThread, INFINITE);
                EnterCriticalSection(&gDrvCS);

                //
                // Traverse DRVINFO linked list, creating drive buttons and
                //   allocating corresp. structures as necessary.
                //
                lpWalk = glpDrives;
                lpBWalk = lpDrvButtonRoot;

                while( lpWalk != NULL ){
                    if( lpBWalk == NULL ){ //If at the end of the button list

                        // Allocate a LPBINFO (button) structure
                        EnterCriticalSection(&gHeapCS);
                        lpBWalk = (LPBINFO)HeapAlloc(ghHeap, 0, (DWORD)sizeof(BINFO) );
                        LeaveCriticalSection(&gHeapCS);

                        // Create a button window
                        lpBWalk->hButton = (HANDLE)CreateWindow(TEXT("BUTTON"),
                                                 lpWalk->DriveName,
                                                 WS_CHILD | WS_VISIBLE |
                                                 BS_OWNERDRAW,
                                                 xVal + DRIVE_BITMAP_SPACING,
                                                 yVal,
                                                 DRIVE_BITMAP_WIDTH,
                                                 DRIVE_BITMAP_HEIGHT,
                                                 hwnd,
                                                 (HMENU)nCount,
                                                 ghModule,

NULL);

                        // Insert structure into list
                        if( lpDrvButtonRoot == NULL)
                            lpDrvButtonRoot = lpBHold = lpBWalk;
                        else{
                            lpBHold->next = lpBWalk;
                            lpBWalk->next = NULL;
                        }

                    }

                    // An LPBINFO (button) structure exists: now initialize

                    // Set Title of Button (Drive Letter)
                    SetWindowText(lpBWalk->hButton, lpWalk->DriveName);

                    // Set Child Window ID for Button
                    SetMenu( lpBWalk->hButton, (HMENU)nCount);

                    // Determine button up/down status
                    if( lpCInfo->lpDriveInfo == lpWalk ){
                        nActiveDrvIndex = nCount;
                        lpBWalk->fButtonDown = TRUE;
                    }
                    else
                        lpBWalk->fButtonDown = FALSE;

                    // Set a pointer to the corresponding drive in Drive list
                    lpBWalk->lpDrive = lpWalk;

                    nCount++;
                    xVal += nDrvEntryWidth;
                    lpBHold = lpBWalk;
                    lpBWalk = lpBWalk->next;

                    lpWalk = lpWalk->next;
                }

                LeaveCriticalSection(&gDrvCS);

                //
                // Free any remaining button windows.
                //
                while( lpBWalk != NULL ){
                    // NULL out new end of list
                    lpBHold->next = NULL;

                    // Assign pointer to doomed node
                    lpBHold = lpBWalk;
                    lpBWalk = lpBWalk->next;

                    // Free doomed node resources
                    if( !DestroyWindow(lpBHold->hButton) ) {
                        LoadString(ghModule, IDS_DRVBUTTNERR, lpBuffer, sizeof(lpBuffer));
                        ErrorMsg(lpBuffer);
                    }

                    EnterCriticalSection(&gHeapCS);
                    HeapFree(ghHeap, 0, (LPVOID)lpBHold);
                    LeaveCriticalSection(&gHeapCS);
                }

                SendMessage(hwnd, WM_PAINT, (WPARAM)NULL, (LPARAM)NULL);
                break;
              }

              //
              // switches the drive button to the newly active drv child's
              //   current drive.  Called by WM_MOUSEACTIVATE in DrvWndProc,
              //   as well as ChangeDrive.
              //   lParam contains the drive linked list pointer of the active
              //   drv child's LPCINFO struct.
              //
              case MM_ACTIVEDRV:{
                LPBINFO lpBWalk = lpDrvButtonRoot;
                int     nCount = 0;

                //
                // 'unpush' old active button
                //
                for( nCount = MM_DRIVE_NUM; nCount < nActiveDrvIndex; nCount++)
                    lpBWalk = lpBWalk->next;
                lpBWalk->fButtonDown = FALSE;

                InvalidateRect(lpBWalk->hButton, NULL, FALSE);

                //
                // change active drive to new before redrawing old.
                //  'push' new active button
                //
                lpBWalk = lpDrvButtonRoot;
                nCount = MM_DRIVE_NUM;
                while( lpBWalk->lpDrive != (LPDINFO)lParam){
                    lpBWalk = lpBWalk->next;
                    nCount++;
                }

                nActiveDrvIndex = nCount;

                lpBWalk->fButtonDown = TRUE;

                InvalidateRect(lpBWalk->hButton, NULL, FALSE);

                break;
              }

              //
              // Changes drive of active child.  ButtonID in lParam.
              //
              case MM_DRIVE_NUM:{

                LPBINFO lpBWalk = lpDrvButtonRoot;
                int     nCount = 0;
                TCHAR    szDrvBuff[DIRECTORY_STRING_SIZE];

                //
                // if drive chosen is already current drive, leave.
                //
                if( nActiveDrvIndex == (int)lParam )
                    break;

                //
                // unpush' old active button
                //
                for( nCount = MM_DRIVE_NUM; nCount < nActiveDrvIndex; nCount++)
                    lpBWalk = lpBWalk->next;
                lpBWalk->fButtonDown = FALSE;

                //
                // change active drive to new before redrawing old.
                //
                nActiveDrvIndex = (int)lParam;

                InvalidateRect(lpBWalk->hButton, NULL, FALSE);

                //
                // 'push' new active button
                //
                lpBWalk = lpDrvButtonRoot;

                for( nCount = MM_DRIVE_NUM; nCount < nActiveDrvIndex; nCount++)
                    lpBWalk = lpBWalk->next;
                lpBWalk->fButtonDown = TRUE;

                InvalidateRect(lpBWalk->hButton, NULL, FALSE);

                GetWindowText(lpBWalk->hButton, szDrvBuff,
                              DIRECTORY_STRING_SIZE);

                if( !ChangeDrive(szDrvBuff, (DWORD)nActiveDrvIndex) ){
                    LoadString(ghModule, IDS_CHNGDRVERR, lpBuffer, sizeof(lpBuffer));
                    ErrorMsg(lpBuffer);
                    return(0);
                }

                break;
              }
            }
            return(1);
        }

        //
        // Sent by all created buttons for initialization purposes.
        //
        case WM_MEASUREITEM:{
            LPMEASUREITEMSTRUCT lpMIS;

            lpMIS = (LPMEASUREITEMSTRUCT)lParam;

            lpMIS->CtlType = ODT_BUTTON;
            lpMIS->CtlID = (UINT)wParam;
            lpMIS->itemWidth = DRIVE_BITMAP_WIDTH;
            lpMIS->itemHeight = DRIVE_BITMAP_HEIGHT;

            return(1);
        }

        //
        // Sent by owner draw drive buttons when needing redrawing.
        //
        case WM_DRAWITEM:{
            LPBINFO lpBWalk = lpDrvButtonRoot;
            int     nCount;
            int     nBmpIndex;
            HDC     hDC;
            HDC     hCompatDC;
            HGDIOBJ hOldBitmap;
            TCHAR    szDrvBuff[DIRECTORY_STRING_SIZE];
            LPDRAWITEMSTRUCT    lpDIS;

            lpDIS = (LPDRAWITEMSTRUCT)lParam;

            for( nCount = MM_DRIVE_NUM; nCount < (int)wParam; nCount++)
                lpBWalk = lpBWalk->next;

            //
            // If not the current selected button, handle button stuff.
            //
            if( (int)wParam != nActiveDrvIndex ){
                //
                // mousebutton is down...
                //
                if( lpDIS->itemAction & ODA_SELECT ){
                    //
                    // left button region, 'unpush' button
                    //
                    if( lpDIS->itemState == (UINT)ODS_FOCUS )
                        lpBWalk->fButtonDown = FALSE;
                    //
                    // clicked on a button, draw 'pushed' button
                    //
                    if( lpDIS->itemState == (UINT)(ODS_SELECTED | ODS_FOCUS))
                        lpBWalk->fButtonDown = TRUE;
                }
            }

            //
            // draw current state of button.
            //
            GetWindowText(lpDIS->hwndItem, szDrvBuff,
                          DIRECTORY_STRING_SIZE);

            szDrvBuff[1] = TEXT('\0');

            hCompatDC = CreateCompatibleDC(lpDIS->hDC);
            hOldBitmap = CreateCompatibleBitmap(hCompatDC,
                                                DRIVE_BITMAP_WIDTH,
                                                DRIVE_BITMAP_HEIGHT);

            nBmpIndex = GetDriveBitmap(lpBWalk);

            SelectObject( hCompatDC, hDrvBmp[nBmpIndex] );

            if( !hOldBitmap ) {
                LoadString(ghModule, IDS_SELOBJERR, lpBuffer, sizeof(lpBuffer));
                ErrorMsg(lpBuffer);
            }
            if( !BitBlt(lpDIS->hDC, lpDIS->rcItem.left, lpDIS->rcItem.top,
                   DRIVE_BITMAP_WIDTH,
                   DRIVE_BITMAP_HEIGHT,
                   hCompatDC, 0, 0, SRCCOPY) ) {
                LoadString(ghModule, IDS_BITBLTERR, lpBuffer, sizeof(lpBuffer));
                ErrorMsg(lpBuffer);
            }

            SelectObject( hCompatDC, hOldBitmap);
            DeleteDC(hCompatDC);

            hDC = GetDC(hwnd);
            SetBkMode(hDC, TRANSPARENT);
            SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT) );
            SetBkColor(hDC, GetSysColor(COLOR_BTNSHADOW) );

            // Allow for a different aesthetic for Japanese systems
            if (GetUserDefaultLangID() == LANG_JAPANESE)
                TextOut(hDC,
                        ((int)(wParam - MM_DRIVE_NUM) * nDrvEntryWidth) +
                            DRIVE_BITMAP_SPACING + DRIVE_BITMAP_WIDTH +
                            DRIVE_LETTER_SPACING,
                        GetSystemMetrics(SM_CYBORDER)/2,
                        szDrvBuff, 1);
            else
                TextOut(hDC,
                        ((int)(wParam - MM_DRIVE_NUM) * nDrvEntryWidth) +
                            DRIVE_BITMAP_SPACING + DRIVE_BITMAP_WIDTH +
                            DRIVE_LETTER_SPACING,
                        (GetSystemMetrics(SM_CYBORDER) + 6)/2,
                        szDrvBuff, 1);

            SetBkMode(hDC, OPAQUE);

            ReleaseDC(hwnd, hDC);

            break;
        }

        case WM_PAINT:{
            HDC     hCompatDC;
            RECT     rc;
            PAINTSTRUCT ps;

            //
            // Paint btnshadow background.
            //
            GetClientRect(hwnd, &rc);

            BeginPaint(hwnd, &ps);

            hCompatDC = CreateCompatibleDC(ps.hdc);
            FillRect(ps.hdc, &rc, hBrush);

            EndPaint(hwnd, &ps);

            return(TRUE);
        }

        case WM_DESTROY:{
            DeleteObject(hBrush);

            for(yVal = 0; yVal < NUM_BITMAPS; yVal++)
                DeleteObject(hDrvBmp[yVal]);

            break;
        }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

/***************************************************************************\
*
* GetDriveBitmap()
*
* Determines the appropriate index into the drive button bitmap array
*   (hDrvBmp[]), given a pointer to a drive info structure (LPDINFO)
*
*   lpWalk          -   pointer to LPDINFO structure.
*   lpCurrentDrv    -   pointer to current drive of active child.
*
*
* History:
* 6/16/92
*   Created.
*
\***************************************************************************/
int GetDriveBitmap(LPBINFO lpBWalk)
{
    int nBmpIndex;

    EnterCriticalSection(&gDrvCS);

    switch( lpBWalk->lpDrive->DriveType ){
        case DRIVE_REMOVABLE:{
          nBmpIndex = UB_FLOPPY1 - UB_BMP_MARKER;
          break;
        }

        case DRIVE_REMOTE:{
          nBmpIndex = UB_REMOTE1 - UB_BMP_MARKER;
            break;
        }

        case DRIVE_CDROM:{
          nBmpIndex = UB_CD1 - UB_BMP_MARKER;
            break;
        }

        case DRIVE_FIXED:
        default:{
          nBmpIndex = UB_FIXED1 - UB_BMP_MARKER;
            break;
        }
    }

    LeaveCriticalSection(&gDrvCS);

    if( lpBWalk->fButtonDown == TRUE )
        nBmpIndex++;

    return(nBmpIndex);
}

/***************************************************************************\
*
* ChangeDrive()
*
*   Changes the current drive of the active child.  Called by the MM_DRIVE_NUM
*     cases in MainWndProc and DriveBarProc.  This is caused by choosing a
*     Drive menu item or selecting a drive button from the drive toolbar.
*
*     lpszDriveName -   points to a buffer containing the name of the drive
*     DriveID       -   points to the ID of the Menu item or button, which
*                         corresponds to the index into the drives linked list
*                         of the new drive.
*
* History:
* 6/20/92
*   Created.
*
\***************************************************************************/
BOOL ChangeDrive(LPTSTR lpszDriveName, DWORD DriveIndex)
{
    LPCINFO     lpCInfo;
    LPDINFO     lpWalk;
    DWORD       dwLoop;
    UINT        nDriveType;
    TCHAR       lpBuffer[128];

    //
    // Retrieve active child handle.
    //
    if( (ghActiveChild != ghwndDrv1) &&
        (ghActiveChild != ghwndDrv2) ){
        LoadString(ghModule, IDS_DRVNOTACTVE, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    //
    // Retrieving the child window's DRVCHILDINFO data
    //
    lpCInfo = (LPCINFO)GetWindowLong(ghActiveChild, GWL_USERDATA);

    //
    // Enter Drive list critical section
    //
    EnterCriticalSection(&gDrvCS);

    //
    // if removable drive, check for existing media.
    //
    nDriveType = GetDriveType(lpszDriveName);
    if( nDriveType == DRIVE_REMOVABLE ||
        nDriveType == DRIVE_CDROM ){
        dwLoop = (DWORD)IDOK;

        LoadString(ghModule, IDS_INSRTMEDIA, lpBuffer, sizeof(lpBuffer));

        while( !CheckRM(lpszDriveName) && (dwLoop == (DWORD)IDOK) ){
           dwLoop = (DWORD)MessageBox(ghwndMain, lpBuffer, lpszDriveName, MB_OKCANCEL);
        }

        if( dwLoop == (DWORD)IDCANCEL ){
            SendMessage(ghwndDrives, WM_COMMAND, MM_ACTIVEDRV,
                        (LPARAM)lpCInfo->lpDriveInfo);
        LeaveCriticalSection(&gDrvCS);
            return(0);
        }
    }

    //
    // set lpDriveInfo member to associated drive struct.
    //
    lpWalk = glpDrives;
    for( dwLoop = 0; dwLoop < DriveIndex - MM_DRIVE_NUM;
         dwLoop++)
         lpWalk = lpWalk->next;

    lpCInfo->lpDriveInfo = lpWalk;

    lstrcpy(lpCInfo->CaptionBarText, lpWalk->DriveName);

    LeaveCriticalSection(&gDrvCS);

    //
    // This will terminate any currently running drive thread.
    //
    SendMessage(ghActiveChild, WM_COMMAND, MM_ESCAPE, (LPARAM)0);
    lpCInfo->fEscape = FALSE;

    //
    // enact the drive change.
    //
    PostMessage(ghActiveChild, WM_COMMAND, MM_REFRESH, (LPARAM)0);

    return(1);
}

/***************************************************************************\
*
* FunctionBarProc
*
* ToolBar Window procedure for displaying File I/O functions.
*   ghwndFunction is the global handle assoc. w/ this Dlg procedure.
*
* History:
* 6/8/92
*   Created.
*
\***************************************************************************/
LRESULT WINAPI FunctionBarProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HBRUSH   hBrush;

    switch (message){
      case WM_INITDIALOG:{
        HWND    hButton;
        RECT     rc;

        hButton = GetDlgItem(hDlg, MM_COPY);

        GetWindowRect(hButton, &rc);

        SetWindowLong(hDlg, GWL_USERDATA, rc.bottom - rc.top);

        //
        // Sets background color of Toolbar non-modal dialog children.
        //
        hBrush = CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));

        return(FALSE);
      }

      case WM_PAINT:{
        RECT     rc;
        PAINTSTRUCT ps;

        //
        // Paint btnshadow background.
        //
        GetClientRect(hDlg, &rc);
        InvalidateRect(hDlg, &rc, FALSE);

        BeginPaint(hDlg, &ps);

        FillRect(ps.hdc, &rc, hBrush);

        EndPaint(hDlg, &ps);

        return(TRUE);
      }

      //
      // Passes button messages ( = file I/O function messages )
      //   to active Drv child.
      //
      case WM_COMMAND:{
        switch(wParam){
          case MM_COPY:
          case MM_MOVE:
          case MM_DELETE:
          case MM_RENAME:
          case MM_MKDIR:{
            SendMessage(ghActiveChild, message, wParam, lParam);
            return(TRUE);
          }
        }
      }

      case WM_DESTROY:{
          DeleteObject(hBrush);
          break;
      }
    }

    return(FALSE);
}

/***************************************************************************\
*
* RunCommandItem()
*
*
* History:
* 5/26/93
*   Created.
*
\***************************************************************************/
BOOL RunCommandItem(LPCINFO lpCInfo)
{
    TCHAR       szCmdLine[DIRECTORY_STRING_SIZE * 2] = TEXT("cmd ");
    LPTSTR      lpszHold;
    TCHAR       lpBuffer[128];

    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    //
    // Add the CMD.EXE parameter to keep or kill cmd sessions when done.
    //
    if( gfKeepCommandWin )
        lstrcat( szCmdLine, TEXT("/k ") );
    else
        lstrcat( szCmdLine, TEXT("/c ") );

    //
    // Add command line edit control text.
    //
    lpszHold = TStrChr(szCmdLine, TEXT('\0'));

    if( SendMessage( ghwndCommand, WM_GETTEXT,
                            DIRECTORY_STRING_SIZE,
                            (LPARAM)lpszHold) == LB_ERR ){
        LoadString(ghModule, IDS_SRCSTRNGERR, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    //
    // Attempt to spawn command shell with entry as parameter.
    //
    si.cb = sizeof(STARTUPINFO);
    si.lpReserved = NULL;
    si.lpDesktop = NULL;
    si.lpTitle = NULL;
    si.dwFlags = 0;
    si.cbReserved2 = 0;
    si.lpReserved2 = NULL;

    SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

    if( !CreateProcess(NULL, (LPTSTR)szCmdLine, NULL, NULL, FALSE,
                       CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS,
                       NULL, lpCInfo->CaptionBarText, &si, &pi) ){
        LoadString(ghModule, IDS_CANTSPAWN, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(0);
    }

    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );

    SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL);

    return(1);
}

/***************************************************************************\
*
* UpdateDrivesMenu()
*
* Adds current drives from the glpDrives linked list to the TEXT('Drives') menu
*
* Input: hDrivesMenu - handle to TEXT('Drives') Menu
*        hThread    - used to wait for drives thread to terminate
*
* History:
* 5/14/92
*   Created.
*
\***************************************************************************/
BOOL UpdateDrivesMenu(HMENU hMenu, HANDLE hThread)
{
    HMENU   hDrivesMenu;
    int     NumMenuItems;
    DWORD   dwLoop;
    LPDINFO lpWalk;
    TCHAR   lpBuffer[128];

    //
    // Remove list of drive menu items from Drive menu, if any.
    //
    hDrivesMenu = GetSubMenu( hMenu, DRIVE_MENU_NUM);
    if( !hDrivesMenu ){
        LoadString(ghModule, IDS_MENUERR1, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
        return(FALSE);
    }

    if( (NumMenuItems = GetMenuItemCount(hDrivesMenu)) == -1) {
        LoadString(ghModule, IDS_MENUERR2, lpBuffer, sizeof(lpBuffer));
        ErrorMsg(lpBuffer);
    }

    //
    // Delete previous menu items.
    //
    for( dwLoop = 0; dwLoop < (DWORD)NumMenuItems; dwLoop++)
        if( !DeleteMenu( hDrivesMenu, 0,
                         MF_BYPOSITION) ){
            LoadString(ghModule, IDS_MENUERR3, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
            return(FALSE);
        }

    //
    // Wait for Enumdrv Thread to terminate, and
    //   enter drive list critical section
    //
    WaitForSingleObject(hThread, INFINITE);
    EnterCriticalSection(&gDrvCS);

    //
    // Fill drive menu from glpDrives linked list
    //
    NumMenuItems = 0;
    lpWalk = glpDrives;

    while(lpWalk != NULL){
        if( !InsertMenu( hDrivesMenu, NumMenuItems, MF_STRING |
            MF_BYPOSITION | MF_ENABLED, MM_DRIVE_NUM + NumMenuItems,
            lpWalk->DriveName)) {
            LoadString(ghModule, IDS_MENUERR4, lpBuffer, sizeof(lpBuffer));
            ErrorMsg(lpBuffer);
        }
        NumMenuItems++;
        lpWalk = lpWalk->next;
    }

    LeaveCriticalSection(&gDrvCS);

    return(TRUE);
}

/***************************************************************************\
*
* ErrorMsg()
*
*   Displays a Message Box with a given error message.
*
* History:
* 5/28/92
*   Created.
*
\***************************************************************************/
void ErrorMsg(LPTSTR szMsg)
{
    TCHAR szHold[DIRECTORY_STRING_SIZE + 1];

    lstrcpy( szHold, szMsg );
    lstrcat( szHold, TEXT("\n") );

    OutputDebugString(szHold);
}


FILER.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//
// Forward declarations in FILER.C
//
BOOL    InitializeApp           (void);
int     GetDriveBitmap          (LPBINFO);
BOOL    ChangeDrive             (LPTSTR, DWORD);
BOOL    RunCommandItem          (LPCINFO);
BOOL    UpdateDrivesMenu        (HMENU, HANDLE);
void    ErrorMsg                (LPTSTR);

LRESULT WINAPI MainWndProc      (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI AboutProc        (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI DriveBarProc     (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI FunctionBarProc  (HWND, UINT, WPARAM, LPARAM);


GLOBALS.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include<windows.h>

//
// Accellerator IDs
//
#define ACCEL_ID        1001

//
// Icon IDs
//
#define UI_FILERICON    2001

//
// DialogBox Control IDs
//
#define SB_OK           3001
#define SB_CANCEL       3002
#define SB_ACTION       3003
#define SB_TOFROM       3004
#define SB_SOURCE       3005
#define SB_DEST         3006
#define SB_STATIC       3007
#define SB_KEY          3008
#define SB_VALUE        3009
#define SB_SCROLL       3010

//
// String Table IDs
//
#define STR_EXE         4001
#define STR_COM         4002
#define STR_CMD         4003
#define STR_BAT         4004
#define IDS_ABOUTDLGERR 4005
#define IDS_DRVBUTTNERR 4006
#define IDS_CHNGDRVERR  4007
#define IDS_SELOBJERR   4008
#define IDS_BITBLTERR   4009
#define IDS_DRVNOTACTVE 4010
#define IDS_INSRTMEDIA  4011
#define IDS_SRCSTRNGERR 4012
#define IDS_CANTSPAWN   4013
#define IDS_MENUERR1    4014
#define IDS_MENUERR2    4015
#define IDS_MENUERR3    4016
#define IDS_MENUERR4    4017
#defineIDS_ENUMDRVERR4018
#defineIDS_ENUMALOCERR 4019
#defineIDS_ENUMSTRERR4020
#defineIDS_DRVALOCERR4021
#define IDS_DCCDRVLST4022
#define IDS_DCCCURDIR   4023
#define IDS_DCCTABSTP   4024
#define IDS_MMOPENERR   4025
#define IDS_COPYING     4026
#define IDS_COPYINGTO   4027
#define IDS_DELETING    4028
#define IDS_DELETINGFRM 4029
#define IDS_MOVING      4030
#define IDS_RENAMEERR   4031
#define IDS_MKDIRERR    4032
#define IDS_VERDLGERR   4033
#define IDS_MMEXPAND    4034
#define IDS_REFRESH1    4035
#define IDS_REFRESH2    4036
#define IDS_REFRESH3    4037
#define IDS_FILDIRERR   4038
#define IDS_FILDIRERR2  4039
#define IDS_FILDIRERR3  4040
#define IDS_FILDIRERR4  4041
#define IDS_DBLCLKERR   4042
#define IDS_SELCNGERR   4043
#define IDS_CONDIRERR   4044
#define IDS_LBNOTFYERR  4045
#define IDS_PARNTFYERR  4046
#define IDS_GETLBTXTERR 4047
#define IDS_OPENLBERR   4048
#define IDS_CANTEDIT    4050
#define IDS_FILE        4051
#define IDS_DIR         4052
#define IDS_GETVERERR   4053
#define IDS_GETVERERR2  4054
#define IDS_GETVERERR3  4055
#define IDS_GETVERERR4  4056
#define IDS_GETVERERR5  4057
#define IDS_GETVERERR6  4058
#define IDS_GETVERERR7  4059
#define IDS_GETVERERR8  4060
#define IDS_ISDIRERR    4061
#define IDS_EXEFILEERR  4062
#define IDS_SELPROCERR  4063
#define IDS_SELDLGERR   4064
#define IDS_SELDLGERR2  4065
#define IDS_SELDLGERR3  4066
#define IDS_SELDLGERR4  4067
#define IDS_SELDLGERR5  4068
#define IDS_SELDLGERR6  4069
#define IDS_SELDLGERR7  4070
#define IDS_FILEIOERR   4071
#define IDS_FILEIOERR2  4072
#define IDS_FILEIOERR3  4073
#define IDS_HNDIOMSG    4074
#define IDS_HNDIOMSG2   4075
#define IDS_HNDIOMSG3   4076
#define IDS_HNDIOMSG4   4077
#define IDS_HNDIOMSG5   4078
#define IDS_RENMEMSG    4079
#define IDS_RENMEMSG2   4080
#define IDS_RENMEMSG3   4081
#define IDS_RENMEMSG4   4082
#define IDS_RENMEMSG5   4083
#define IDS_RENMEMSG6   4084
#define IDS_RENMEMSG7   4085
#define IDS_MKDIRMSG    4086
#define IDS_MKDIRMSG2   4087
#define IDS_MKDIRMSG3   4088
#define IDS_TXTMSG      4089

#define STR_DEF_EDITOR  4101

//
// Bitmap IDs
//
#define UB_BMP_MARKER   5001    // This should be the same as the first bitmap.
#define UB_FLOPPY1      5001    // THESE BITMAP IDs MUST BE CONTIGUOUS AND
#define UB_FLOPPY2      5002    //   IN THIS ORDER!  For 'ease of loading'.
#define UB_REMOTE1      5003
#define UB_REMOTE2      5004
#define UB_CD1          5005
#define UB_CD2          5006
#define UB_FIXED1       5007
#define UB_FIXED2       5008

//
// Command line window and Drv-CHILD child window IDs
//
#define COMMAND_ID      6001    // Command Line Window
#define TEXT_WINDOW_ID  6002    // Title Bar
#define LISTDIR_ID      6003    // Directory ListBox
#define LISTFILE_ID     6004    // File ListBox

//
// Internal Main window IDs
//
#define MM_FILLDIR      7001
#define MM_FILLFILE     7002

//
// Menu ID's, VKEYs, & other WM_COMMAND messages
//
#define MM_OPEN         8001
#define MM_COPY         8002
#define MM_DELETE       8003
#define MM_MOVE         8004
#define MM_RENAME       8005
#define MM_MKDIR        8006
#define MM_DELDIR       8007
#define MM_VERSION      8008
#define MM_EXIT         8009

#define MM_REFRESH      8101
#define MM_ACTIVEDRV    8102
#define MM_TAB          8103
#define MM_SWAP         8104
#define MM_ORIENT       8105
#define MM_EXPAND       8106
#define MM_KEEPCMD      8107

#define MM_ESCAPE       8112
#define MM_TOGGLE       8113

#define MM_ABOUT        8201

#define MM_DRIVE_NUM    8301       // ...Through 8226, one for each drive letter
                    //   .
                    //   .
                    //  8326

//
// constants
//
#define MAIN_WIDTH          605    // Default width and height of main window
#define MAIN_HEIGHT         400

#define DRIVE_BITMAP_WIDTH  23     // Width and height of drive toolbar bitmaps
#define DRIVE_BITMAP_HEIGHT 14     //   assoc. w/ bitmap IDs above.
#define DRIVE_LETTER_SPACING 2     // Space between drv bmp and drv letter label
#define DRIVE_BITMAP_SPACING 6     // Space on either side of drv bitmap-labels
#define NUM_BITMAPS          8     // # types of Drive Bitmaps to be loaded.

#define DIRECTORY_STRING_SIZE MAX_PATH  // Size of max allowable direcory path

#define LIST_BORDER         2      // Space between listbox and Drv child frame

#define DRIVE_MENU_NUM      2      // This is the 'Drives' submenu

#define LISTBOX_TAB_SIZE    8      // Size of the tabs in the Drv child LBs

#define OVER_UNDER          0      // Relative positions of Drv children
#define SIDE_BY_SIDE        1

#define BOLD_FONT           700    // GetDeviceCaps sez this value is for bold
                                   //   in the lfWeight LOGFONT member

#define NUM_EXTENSION_STRINGS   4  // Lists number of file ext. in stringtable
#define EXTENSION_LENGTH        5  // Tot. number of characters in extension,
                                   //  including '\0' char.

#define FILE_SYSTEM_STRING_SIZE         20      // Must be >= 8

#define NUM_POSSIBLE_REMOVABLE_DRIVES   26      // A-Z

#define MUTEX_TIMEOUT       50     // LB Mutex timeout in milliseconds

// The following are used in the .EXE Version retrieval code in DRVPROC.C
#define NUM_VERSION_INFO_KEYS    12     // See FilerGetVersion()
#define VERSION_DLG_LB_HEIGHT    10     // height in text lines of version dlg LBs

#define VERSION_INFO_KEY_ROOT    TEXT("\\StringFileInfo\\")
// end .EXE Version code constants

//
// Drive enumeration strucure
//
typedef struct _PerDriveInfo {
    TCHAR   DriveLetter;
    LPTSTR  DriveName;
    TCHAR   FileSystem[FILE_SYSTEM_STRING_SIZE];
    UINT    DriveType;
    struct _PerDriveInfo *next;
} DRVINFO, *LPDINFO;

//
// Instance data for each Drv child window
//
typedef struct _PerWndInfo {
    HWND    hwnd;                       // Handle to Drive child window
    HWND    hParent;                    // Handle to Filer main window
    HWND    hTextWnd;                   // Handle to Drive child Title Bar
    HWND    hFileLB;                    // Handle to Drv child File LB
    HWND    hDirLB;                     // Handle to Drv child Dir LB
    HANDLE  hDirThread;                 // Handle to ExpDir or FullExpand thread
    HANDLE  hDirMutex;                  // Protects Dir LB from mult. access
    HANDLE  hFileMutex;                 // Protects File LB from mult. access
    BOOL    fDirLeft;                   // Flag: the Dir LB is on left
    BOOL    fDirExpand;                 // Flag: the Dir LB is fully expanded
    BOOL    fSuicide;                   // Flag: kills ExpDir/FullExpand thread
    BOOL    fEscape;                    // Flag: User aborted Expand thread
    LPDINFO lpDriveInfo;                // pointer to current _PerDriveInfo
    TCHAR   CaptionBarText[DIRECTORY_STRING_SIZE]; // Drv Child Title text
} DRVCHILDINFO, *LPCINFO;

typedef struct _PerButtonInfo {
    HANDLE          hButton;
    BOOL            fButtonDown;
    LPDINFO         lpDrive;
    struct _PerButtonInfo  *next;
}   BINFO, *LPBINFO;

typedef struct _SelectDlgInfo {
    HWND    hwnd;
    DWORD   dwAction;
    LPTSTR  szAction;
    LPTSTR  szToFrom;
} SELECTINFO, *LPSINFO;

//  .EXE version information key structure: for FilerGetVersion() in DRVPROC.C
typedef struct _VersionKeyInfo {
    TCHAR const *szKey;
    TCHAR       *szValue;
} VKINFO, *LPVKINFO;


README.TXT

Accessing a Floppy Disk

SUMMARY
========

The FLOPPY sample is designed to show how to access a physical floppy disk
under Windows NT.

This program has two major features:

  - It can be used to display the geometry of a disk. For example, use the
    command:

        mfmt -g a:

  - It can be used to produce a disk image, or to write a disk image to a
    floppy. For example, use the commands:

        mfmt -c a: bootdisk         - produce a disk image of a:
        mfmt -c bootdisk a:         - make a: identical to bootdisk image

MORE INFORMATION
=================

This program is very simple. Minimal error checking is done. It is meant to
provide an example of how to:

  - Open a physical disk.

  - Read a disk's geometry.

  - Perform a low-level format operation.

  - Read and write physical sectors.


MFMT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*++

Module Name:

    mfmt.c

Abstract:

    This program is designed to show how to access a physical floppy
    disk using the Win32 API set.

    This program has two major functions.

        - It can be used to display the geometry of a disk

            mfmt -g a:

        - It can be used to produce a disk image, or to write a disk
          image to a floppy.

            mfmt -c a: bootdisk         - produce a disk image of a:

            mfmt -c bootdisk a:         - make a: identical to bootdisk image

    This program is very very simple. Minimal error checking is done. It is
    meant to provide an example of how to:

        - Open a physical disk

        - Read a disk's geometry

        - Perform a low level format operation

        - read and write physical sectors

--*/

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <winioctl.h>
#include <string.h>
#include <ctype.h>
#include <memory.h>

DISK_GEOMETRY SupportedGeometry[20];
DWORD SupportedGeometryCount;

BOOL
GetDiskGeometry(
    HANDLE hDisk,
    PDISK_GEOMETRY lpGeometry
    )

{
    DWORD ReturnedByteCount;

    return DeviceIoControl(
                hDisk,
                IOCTL_DISK_GET_DRIVE_GEOMETRY,
                NULL,
                0,
                lpGeometry,
                sizeof(*lpGeometry),
                &ReturnedByteCount,
                NULL
                );
}

DWORD
GetSupportedGeometrys(
    HANDLE hDisk
    )
{
    DWORD ReturnedByteCount;
    BOOL b;
    DWORD NumberSupported;

    b = DeviceIoControl(
                hDisk,
                IOCTL_DISK_GET_MEDIA_TYPES,
                NULL,
                0,
                SupportedGeometry,
                sizeof(SupportedGeometry),
                &ReturnedByteCount,
                NULL
                );
    if ( b ) {
        NumberSupported = ReturnedByteCount / sizeof(DISK_GEOMETRY);
        }
    else {
        NumberSupported = 0;
        }
    SupportedGeometryCount = NumberSupported;

    return NumberSupported;
}

VOID
PrintGeometry(
    LPSTR lpDriveName,
    PDISK_GEOMETRY lpGeometry
    )
{
    LPSTR MediaType;

    if (lpDriveName) {
        printf("Geometry for Drive %s\n",lpDriveName);
        }

    switch ( lpGeometry->MediaType ) {
        case F5_1Pt2_512:  MediaType = "5.25, 1.2MB,  512 bytes/sector";break;
        case F3_1Pt44_512: MediaType = "3.5,  1.44MB, 512 bytes/sector";break;
        case F3_2Pt88_512: MediaType = "3.5,  2.88MB, 512 bytes/sector";break;
        case F3_20Pt8_512: MediaType = "3.5,  20.8MB, 512 bytes/sector";break;
        case F3_720_512:   MediaType = "3.5,  720KB,  512 bytes/sector";break;
        case F5_360_512:   MediaType = "5.25, 360KB,  512 bytes/sector";break;
        case F5_320_512:   MediaType = "5.25, 320KB,  512 bytes/sector";break;
        case F5_320_1024:  MediaType = "5.25, 320KB,  1024 bytes/sector";break;
        case F5_180_512:   MediaType = "5.25, 180KB,  512 bytes/sector";break;
        case F5_160_512:   MediaType = "5.25, 160KB,  512 bytes/sector";break;
        case RemovableMedia: MediaType = "Removable media other than floppy";break;
        case FixedMedia:   MediaType = "Fixed hard disk media";break;
        default:           MediaType = "Unknown";break;
    }
    printf("    Media Type %s\n",MediaType);
    printf("    Cylinders %d Tracks/Cylinder %d Sectors/Track %d\n",
        lpGeometry->Cylinders.LowPart,
        lpGeometry->TracksPerCylinder,
        lpGeometry->SectorsPerTrack
        );
}

BOOL
LowLevelFormat(
    HANDLE hDisk,
    PDISK_GEOMETRY lpGeometry
    )
{
    FORMAT_PARAMETERS FormatParameters;
    PBAD_TRACK_NUMBER lpBadTrack;
    UINT i;
    BOOL b;
    DWORD ReturnedByteCount;

    FormatParameters.MediaType = lpGeometry->MediaType;
    FormatParameters.StartHeadNumber = 0;
    FormatParameters.EndHeadNumber = lpGeometry->TracksPerCylinder - 1;
    lpBadTrack = (PBAD_TRACK_NUMBER) LocalAlloc(LMEM_ZEROINIT,lpGeometry->TracksPerCylinder*sizeof(*lpBadTrack));

    for (i = 0; i < lpGeometry->Cylinders.LowPart; i++) {

        FormatParameters.StartCylinderNumber = i;
        FormatParameters.EndCylinderNumber = i;

        b = DeviceIoControl(
                hDisk,
                IOCTL_DISK_FORMAT_TRACKS,
                &FormatParameters,
                sizeof(FormatParameters),
                lpBadTrack,
                lpGeometry->TracksPerCylinder*sizeof(*lpBadTrack),
                &ReturnedByteCount,
                NULL
                );

        if (!b ) {
            LocalFree(lpBadTrack);
            return b;
            }
        }

    LocalFree(lpBadTrack);

    return TRUE;
}

BOOL
LockVolume(
    HANDLE hDisk
    )
{
    DWORD ReturnedByteCount;

    return DeviceIoControl(
                hDisk,
                FSCTL_LOCK_VOLUME,
                NULL,
                0,
                NULL,
                0,
                &ReturnedByteCount,
                NULL
                );
}

BOOL
UnlockVolume(
    HANDLE hDisk
    )
{
    DWORD ReturnedByteCount;

    return DeviceIoControl(
                hDisk,
                FSCTL_UNLOCK_VOLUME,
                NULL,
                0,
                NULL,
                0,
                &ReturnedByteCount,
                NULL
                );
}

BOOL
DismountVolume(
    HANDLE hDisk
    )
{
    DWORD ReturnedByteCount;

    return DeviceIoControl(
                hDisk,
                FSCTL_DISMOUNT_VOLUME,
                NULL,
                0,
                NULL,
                0,
                &ReturnedByteCount,
                NULL
                );
}

DWORD
_cdecl
main(
    int argc,
    char *argv[],
    char *envp[]
    )
{
    char Drive[MAX_PATH];
    HANDLE hDrive, hDiskImage;
    DISK_GEOMETRY Geometry;
    UINT i;
    char c, *p;
    LPSTR DriveName;
    BOOL fUsage = TRUE;
    BOOL fShowGeometry = FALSE;
    BOOL fDiskImage = FALSE;
    BOOL SourceIsDrive;
    LPSTR Source, Destination, DiskImage;

    if ( argc > 1 ) {
        fUsage = FALSE;
        while (--argc > 0 ) {
            p = *++argv;
            if (*p == '/' || *p == '-') {
                while (c = *++p)
                switch (toupper( c )) {
                case '?':
                    fUsage = TRUE;
                    break;

                case 'C':
                    fDiskImage = TRUE;
                    argc--, argv++;
                    Source = *argv;
                    argc--, argv++;
                    Destination = *argv;
                    break;

                case 'G':
                    argc--, argv++;
                    if ( (DriveName = *argv ) && *DriveName &&
                         isalpha(*DriveName) )
                        fShowGeometry = TRUE;
                    else
                                                {
                        printf( "MFMT: Missing drive letter after -G\n" );
                                    DriveName = NULL;
                        fUsage = TRUE;
                        }
                            break;

                default:
                    printf("MFMT: Invalid switch - /%c\n", c );
                    fUsage = TRUE;
                    break;
                    }
                }
            }
        }

    if ( fUsage ) {
        printf("usage: MFMT switches \n" );
        printf("            [-?] display this message\n" );
        printf("            [-g drive] shows disk geometry\n" );
        printf("            [-c source destination] produce diskimage\n" );
        ExitProcess(1);
        }

    if ( fShowGeometry ) {
        sprintf(Drive,"\\\\.\\%s",DriveName);
        hDrive = CreateFile(
                    Drive,
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ|FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL
                    );
        if ( hDrive == INVALID_HANDLE_VALUE ) {
            printf("MFMT: Open %s failed %d\n",DriveName,GetLastError());
            ExitProcess(1);
            }

        if ( LockVolume(hDrive) == FALSE ) {
            printf("MFMT:Locking volume %s failed %d\n", DriveName, GetLastError());
            ExitProcess(1);
            }

        if ( !GetDiskGeometry(hDrive,&Geometry) ) {
            printf("MFMT: GetDiskGeometry %s failed %d\n",DriveName,GetLastError());
            ExitProcess(1);
            }
        PrintGeometry(DriveName,&Geometry);

        if ( !GetSupportedGeometrys(hDrive) ) {
            printf("MFMT: GetSupportedGeometrys %s failed %d\n",DriveName,GetLastError());
            ExitProcess(1);
            }
        printf("\nDrive %s supports the following disk geometries\n",DriveName);

        for(i=0;i<SupportedGeometryCount;i++) {
            printf("\n");
            PrintGeometry(NULL,&SupportedGeometry[i]);
            }

        printf("\n");
        ExitProcess(0);
        }

    if ( fDiskImage ) {
        SourceIsDrive = FALSE;
        if ( Source[strlen(Source)-1] == ':' ) {
            SourceIsDrive = TRUE;
            sprintf(Drive,"\\\\.\\%s",Source);
            DriveName=Source;
            DiskImage = Destination;
            }
        if ( Destination[strlen(Destination)-1] == ':' ) {
            if ( SourceIsDrive ) {
                printf("MFMT: Source and Destination cannot both be drives\n");
                ExitProcess(1);
                }
            SourceIsDrive = FALSE;
            sprintf(Drive,"\\\\.\\%s",Destination);
            DriveName=Destination;
            DiskImage = Source;
            }
        else {
            if ( !SourceIsDrive ) {
                printf("MFMT: Either Source or Destination must be a drive\n");
                ExitProcess(1);
                }
            }

        //
        // Open and Lock the drive
        //

        hDrive = CreateFile(
                    Drive,
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ|FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL
                    );
        if ( hDrive == INVALID_HANDLE_VALUE ) {
            printf("MFMT: Open %s failed %d\n",DriveName,GetLastError());
            ExitProcess(1);
            }
        if ( LockVolume(hDrive) == FALSE ) {
            printf("MFMT: LockVolume %s failed %d\n",DriveName,GetLastError());
            ExitProcess(1);
            }

        if ( !GetDiskGeometry(hDrive,&Geometry) ) {
            printf("MFMT: GetDiskGeometry %s failed %d\n",DriveName,GetLastError());
            ExitProcess(1);
            }

        if ( !GetSupportedGeometrys(hDrive) ) {
            printf("MFMT: GetSupportedGeometrys %s failed %d\n",DriveName,GetLastError());
            ExitProcess(1);
            }

        //
        // Open the disk image file
        //

        hDiskImage = CreateFile(
                        DiskImage,
                        GENERIC_READ | GENERIC_WRITE,
                        0,
                        NULL,
                        SourceIsDrive ? CREATE_ALWAYS : OPEN_EXISTING,
                        0,
                        NULL
                        );
        if ( hDiskImage == INVALID_HANDLE_VALUE ) {
            printf("MFMT: Open %s failed %d\n",DiskImage,GetLastError());
            ExitProcess(1);
            }

        //
        // Now do the copy
        //
        {
            LPVOID IoBuffer;
            BOOL b;
            DWORD BytesRead, BytesWritten;
            DWORD FileSize;
            DWORD VirtBufSize;
            DWORD NumBufs;

            //
            // If we are copying from floppy to file, just do the copy
            // Otherwise, we might have to format the floppy first
            //

            if ( SourceIsDrive ) {

                //
                // Device reads must be sector aligned. VirtualAlloc will
                // garuntee alignment
                //

                NumBufs = Geometry.Cylinders.LowPart;
                VirtBufSize =  Geometry.TracksPerCylinder *
                               Geometry.SectorsPerTrack *
                               Geometry.BytesPerSector;

                IoBuffer = VirtualAlloc(NULL,VirtBufSize,MEM_COMMIT,PAGE_READWRITE);

                if ( !IoBuffer ) {
                    printf("MFMT: Buffer Allocation Failed\n");
                    ExitProcess(1);
                    }

                for ( ;NumBufs > 0; NumBufs-- )
                    {
                    b = ReadFile(hDrive,IoBuffer, VirtBufSize, &BytesRead, NULL);
                    if (b && BytesRead){
                        b = WriteFile(hDiskImage,IoBuffer, BytesRead, &BytesWritten, NULL);
                        if ( !b || ( BytesRead != BytesWritten ) ) {
                            printf("MFMT: Fatal Write Error %d\n",GetLastError());
                            ExitProcess(1);
                            }
                        }
                    else {
                        printf("MFMT: Fatal Read Error %d\n",GetLastError());
                        ExitProcess(1);
                        }
                    }
                }
            else {

                //
                // Check to see if the image will fit on the floppy. If it
                // will, then LowLevelFormat the floppy and press on
                //

                FileSize = GetFileSize(hDiskImage,NULL);

                b = FALSE;
                for(i=0;i<SupportedGeometryCount;i++) {
                    NumBufs = SupportedGeometry[i].Cylinders.LowPart;
                    VirtBufSize =  SupportedGeometry[i].TracksPerCylinder *
                                   SupportedGeometry[i].SectorsPerTrack *
                                   SupportedGeometry[i].BytesPerSector;
                    if ( VirtBufSize*NumBufs >= FileSize ) {

                        IoBuffer = VirtualAlloc(NULL,VirtBufSize,MEM_COMMIT,PAGE_READWRITE);

                        if ( !IoBuffer ) {
                            printf("MFMT: Buffer Allocation Failed\n");
                            ExitProcess(1);
                            }

                        //
                        // Format the floppy
                        //

                        LowLevelFormat(hDrive,&SupportedGeometry[i]);

                        for ( ;NumBufs > 0; NumBufs-- )
                            {
                            b = ReadFile(hDiskImage,IoBuffer, VirtBufSize, &BytesRead, NULL);
                            if (b && BytesRead){
                                b = WriteFile(hDrive,IoBuffer, BytesRead, &BytesWritten, NULL);
                                if ( !b || ( BytesRead != BytesWritten ) ) {
                                    printf("MFMT: Fatal Write Error %d\n",GetLastError());
                                    ExitProcess(1);
                                    }
                                }
                            else {
                                printf("MFMT: Fatal Read Error %d\n",GetLastError());
                                ExitProcess(1);
                                }
                            }
                        b = TRUE;
                        break;
                    }
                    }

                if ( !b ) {
                    printf("MFMT: FileSize %d is not supported on drive %s\n",FileSize,DriveName);
                    ExitProcess(1);
                    }
                }
        }

        //
        // Dismounting forces the filesystem to re-evaluate the media id
        // and geometry. This is the same as popping the floppy in and out
        // of the disk drive
        //

        DismountVolume(hDrive);
        UnlockVolume(hDrive);

        ExitProcess(0);
        }
    return (0);
}


README.TXT

FWATCH demonstrates the usage of the ReadDirectoryChangesW API.

The FWATCH sample will display changes to watched directories and files in
the command window. The directories and files to be watched are specified
in the FWATCH.INI file which can be left in the application directory.

FWATCH.INI is set by default to look at 3 directories:
c:\dir1
c:\dir2
c:\dir3

and three files in each of these directories:
file1.txt
file2.txt
file3.txt

Once FWATCH.EXE is started, open any of the above text files with NOTEPAD, make
some changes to the file, and then save it. You should see a notification in the
FWATCH console window immediately. You will be advised of exactly which file
changed, what the new (current) size is, and when the last write occured.

To exit the application, type "q".

FWATCH creates the directories and files mentioned above. Use File Manager or
a similiar utility to remove the directories/files after using FWATCH.

FWATCH will work only on Windows NT version 3.51 SP3 or greater.


FWATCH.C

/**********************************************************************
   File name: fwatch.c

      Main source file for sample demonstrating use of file change
      notification APIs.

   Functions:

      CheckChangedFile()      - Gets and displays change information
      HandleDirectoryChange() - Watch function
      WatchDirectories()      - Starts the watch
      main()                  - Program main

   Written by Microsoft Product Support Services, Windows Developer Support.
   Copyright 1996 - 1998 Microsoft Corporation. All rights reserved.
**********************************************************************/
#define UNICODE

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>

#define MAX_DIRS    10
#define MAX_FILES   255
#define MAX_BUFFER  4096

// this is the all purpose structure that contains
// the interesting directory information and provides
// the input buffer that is filled with file change data
typedef struct _DIRECTORY_INFO {
HANDLE      hDir;
TCHAR       lpszDirName[MAX_PATH];
CHAR        lpBuffer[MAX_BUFFER];
DWORD       dwBufLength;
OVERLAPPED  Overlapped;
}DIRECTORY_INFO, *PDIRECTORY_INFO, *LPDIRECTORY_INFO;

DIRECTORY_INFO  DirInfo[MAX_DIRS];        // Buffer for all of the directories
TCHAR           FileList[MAX_FILES*MAX_PATH];   // Buffer for all of the files
DWORD           numDirs;

/**********************************************************************
   CheckChangedFile()

   Purpose:
      This function prints out information when one of the files we
      are watching is changed.

   Parameters:

      LPDIRECTORY_INFO lpdi - Information about the watched directory
      PFILE_NOTIFY_INFORMATION lpfni - Information about modified file

   Return Value:
      None

   Comments:

********************************************************************/
void WINAPI CheckChangedFile( LPDIRECTORY_INFO lpdi,
                              PFILE_NOTIFY_INFORMATION lpfni)
{
    TCHAR      szFullPathName[MAX_PATH];
    TCHAR      *p;
    HANDLE     hFile;
    FILETIME   LocalFileTime;
    SYSTEMTIME SystemTime;
    BY_HANDLE_FILE_INFORMATION FileInfo;

    p = FileList;

    while(*p && lstrcmpi(p,lpfni->FileName))
        p+=(lstrlen(p)+1);

    if(*p)
    {
        lstrcpy( szFullPathName, lpdi->lpszDirName );
        lstrcat( szFullPathName, L"\\" );
        lstrcat( szFullPathName, lpfni->FileName );

        // we assume that the file was changed, since
        // that is the only thing we look for in this sample
        wprintf( L"%s changed,", szFullPathName );

        hFile=CreateFile( szFullPathName,
                          GENERIC_READ,
                          FILE_SHARE_READ,
                          NULL,
                          OPEN_EXISTING,
                          FILE_FLAG_SEQUENTIAL_SCAN,
                          0);

        GetFileInformationByHandle( hFile, &FileInfo );

        FileTimeToLocalFileTime( &(FileInfo.ftLastWriteTime), &LocalFileTime );

        FileTimeToSystemTime( &LocalFileTime, &SystemTime );

        wprintf( L" Size = %d bytes,", FileInfo.nFileSizeLow );
        wprintf( L" Last Access = %02d/%02d/%02d %02d:%02d:%02d",
                 SystemTime.wMonth,
                 SystemTime.wDay,
                 SystemTime.wYear,
                 SystemTime.wHour,
                 SystemTime.wMinute,
                 SystemTime.wSecond );

        CloseHandle( hFile );

        wprintf( L"\n" );
    }
}

/**********************************************************************
   HandleDirectoryChanges()

   Purpose:
      This function receives notification of directory changes and
      calls CheckChangedFile() to display the actual changes. After
      notification and processing, this function calls
      ReadDirectoryChangesW to reestablish the watch.

   Parameters:

      HANDLE hCompPort - Handle for completion port

   Return Value:
      None

   Comments:

********************************************************************/
void WINAPI HandleDirectoryChange( DWORD dwCompletionPort )
{
    DWORD numBytes;
    DWORD cbOffset;
    LPDIRECTORY_INFO di;
    LPOVERLAPPED lpOverlapped;
    PFILE_NOTIFY_INFORMATION fni;

    do
    {
        // Retrieve the directory info for this directory
        // through the completion key
        GetQueuedCompletionStatus( (HANDLE) dwCompletionPort,
                                   &numBytes,
                                   (LPDWORD) &di,
                                   &lpOverlapped,
                                   INFINITE);

        if ( di )
        {

            fni = (PFILE_NOTIFY_INFORMATION)di->lpBuffer;

            do
            {
                cbOffset = fni->NextEntryOffset;

                if( fni->Action == FILE_ACTION_MODIFIED )
                    CheckChangedFile( di, fni );

                fni = (PFILE_NOTIFY_INFORMATION)((LPBYTE) fni + cbOffset);

            } while( cbOffset );

            // Reissue the watch command
            ReadDirectoryChangesW( di->hDir,di->lpBuffer,
                                   MAX_BUFFER,
                                   TRUE,
                                   FILE_NOTIFY_CHANGE_LAST_WRITE,
                                   &di->dwBufLength,
                                   &di->Overlapped,
                                   NULL);
        }

    } while( di );

}

/**********************************************************************
   WatchDirectories()

   Purpose:
      This function implements the ReadDirectoryChangesW API for
      indicated directories. For each directory watched, a thread
      is created which waits for changes to the directory. This
      function waits for the user to input 'q', then cleans up and
      quits.

   Parameters:

      HANDLE hCompPort - Handle for completion port

   Return Value:
      None

********************************************************************/
void WINAPI WatchDirectories( HANDLE hCompPort )
{
    DWORD   i;
    DWORD   tid;
    HANDLE  hThread;

    // Start watching each of the directories of interest

    for (i=0;i<numDirs;i++)
    {
        ReadDirectoryChangesW( DirInfo[i].hDir,
                               DirInfo[i].lpBuffer,
                               MAX_BUFFER,
                               TRUE,
                               FILE_NOTIFY_CHANGE_LAST_WRITE,
                               &DirInfo[i].dwBufLength,&DirInfo[i].Overlapped,
                               NULL);
    }

    // Create a thread to sit on the directory changes

    hThread = CreateThread( NULL,
                            0,
                            (LPTHREAD_START_ROUTINE) HandleDirectoryChange,
                            hCompPort,
                            0,
                            &tid);

    // Just loop and wait for the user to quit

    while (getch() != 'q');

    // The user has quit - clean up

    PostQueuedCompletionStatus( hCompPort, 0, 0, NULL );

    // Wait for the Directory thread to finish before exiting

    WaitForSingleObject( hThread, INFINITE );

    CloseHandle( hThread );
}

/**********************************************************************
   main()

   Purpose:
      Main entry-point for fwatch sample. This function reads an .ini
      file (fwatch.ini) to determine which directories and files to
          watch. For each directory watched some information is gathered
      and stored.

   Return Value:  (see errors.h for description)
      None

   Comments:

********************************************************************/
void main(int argc, char *argv[])
{

    TCHAR   *p,*q;                          // Temporary String Pointer
    HANDLE  hCompPort=NULL;                 // Handle To a Completion Port
    DWORD   i;                              // You always need an I.
    TCHAR   DirList[MAX_DIRS*MAX_PATH];     // Buffer for all of the directories
    TCHAR   IniFile[MAX_PATH];
    TCHAR   FilePath[MAX_PATH];
    HANDLE  hFile;

    GetCurrentDirectory( MAX_PATH, IniFile );

    lstrcat( IniFile, L"\\fwatch.ini" );

    GetPrivateProfileString( L"Directories",
                             NULL,
                             NULL,
                             DirList,
                             MAX_DIRS*MAX_PATH,
                             IniFile );

    GetPrivateProfileString( L"Files",
                             NULL,
                             NULL,
                             FileList,
                             MAX_FILES*MAX_PATH,
                             IniFile );

    wprintf( L"Watching these directories:\n" );

    // First, walk through the raw list and count items, creating
    // an array of handles for each directory
    for (p=DirList;*p!='\0';numDirs++,p+=(lstrlen(p)+1))
    {

        if( CreateDirectory( p, NULL ) )
            wprintf( L"Directory %s created\n", p );
        else
            wprintf( L"Directory %s exists\n", p );

        // Get a handle to the directory
        DirInfo[numDirs].hDir = CreateFile( p,
                                            FILE_LIST_DIRECTORY,
                                            FILE_SHARE_READ |
                                            FILE_SHARE_WRITE |
                                            FILE_SHARE_DELETE,
                                            NULL,
                                            OPEN_EXISTING,
                                            FILE_FLAG_BACKUP_SEMANTICS |
                                            FILE_FLAG_OVERLAPPED,
                                            NULL);

        if( DirInfo[numDirs].hDir == INVALID_HANDLE_VALUE )
        {
            wprintf( L"Unable to open directory %s. GLE=%d. Terminating...\n",
                     p, GetLastError() );
            exit( 0 );
        }

        for (q=FileList;*q!='\0';q+=(lstrlen(q)+1))
        {
            lstrcpy( FilePath, p );
            lstrcat( FilePath, L"\\" );
            lstrcat( FilePath, q );

            if( hFile = CreateFile( FilePath,
                                    GENERIC_WRITE,
                                    FILE_SHARE_READ |
                                    FILE_SHARE_WRITE |
                                    FILE_SHARE_DELETE,
                                    NULL,
                                    CREATE_ALWAYS,
                                    FILE_ATTRIBUTE_NORMAL,
                                    NULL) )
            {
                wprintf( L"  File %s created\n", FilePath );
                CloseHandle( hFile );
            }
            else
                wprintf( L"  File %s could not be created\n", FilePath );

        }

        lstrcpy( DirInfo[numDirs].lpszDirName, p );

        // Set up a key(directory info) for each directory
        hCompPort=CreateIoCompletionPort( DirInfo[numDirs].hDir,
                                          hCompPort,
                                          (DWORD) &DirInfo[numDirs],
                                          0);

    }

    wprintf( L"\n\nPress <q> to exit\n" );

    // Start watching
    WatchDirectories( hCompPort );

    for(i=0;i<numDirs;i++)
        CloseHandle( DirInfo[i].hDir );

    CloseHandle( hCompPort );

}


README.TXT

Communications Demonstration

SUMMARY
=======

The COMM sample (TTY) is designed to demonstrate using the Win32
communications functions while maintaining a common code base with
16-bit Windows code.

MORE INFORMATION
================

The program performs communications using the functions OpenFile, ReadFile,
SetCommState, SetCommMask, WaitCommEvent, WriteFile, and CloseFile.

The program creates a background thread to watch for COMM receiver events
and posts a notification message to the main terminal window. Foreground
character processing is written to the communications port.

Simple TTY character translation is performed and a screen buffer is
implemented for use as the I/O window.

Overlapped file I/O techniques are demonstrated.

Usage:

The baud rate, data bits, stop bits, parity, port, RTS/CTS handshaking,
DTR/DSR handshaking, and XON/XOFF handshaking can be changed under the
Settings menu item.

Once the communications settings are set, the Action menu item can be
selected to connect or disconnect the program.


RESOURCE.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define ABOUTDLGBOX        100
#define SETTINGSDLGBOX     101

#define TTYACCEL           200
#define TTYMENU            300
#define TTYICON            400
#define TTYBITMAP          500

#define IDD_OK             1
#define IDD_CANCEL         2

#define IDM_CONNECT        0x100
#define IDM_DISCONNECT     0x101
#define IDM_EXIT           0x102
#define IDM_SETTINGS       0x103
#define IDM_ABOUT          0x104
#define IDM_SENDBLOCK      0x105

#define IDD_ABOUTICON      0x210
#define IDD_TITLELINE      0x211
#define IDD_VERSION        0x212
#define IDD_BYLINE         0x213
#define IDD_WINDOWSMODE    0x214
#define IDD_FREEMEM        0x215
#define IDD_RESOURCES      0x216

#define IDD_PORTCB         0x220
#define IDD_BAUDCB         0x221
#define IDD_DATABITSCB     0x222
#define IDD_PARITYCB       0x223
#define IDD_STOPBITSCB     0x224
#define IDD_DTRDSR         0x225
#define IDD_RTSCTS         0x226
#define IDD_XONXOFF        0x227
#define IDD_AUTOWRAP       0x228
#define IDD_NEWLINE        0x229
#define IDD_LOCALECHO      0x22A
#define IDD_FONT           0x22B
#define IDD_USECNRECEIVE   0x22C
#define IDD_DISPLAYERRORS  0x22D

#define IDS_BYLINE         0x300
#define IDS_MODE_STANDARD  0x301
#define IDS_MODE_ENHANCED  0x302
#define IDS_MODE_WLO       0x303
#define IDS_MODE_UNDEF     0x304

#define IDS_COMPREFIX      0x310

#define IDS_BAUD110        0x320
#define IDS_BAUD300        0x321
#define IDS_BAUD600        0x322
#define IDS_BAUD1200       0x323
#define IDS_BAUD2400       0x324
#define IDS_BAUD4800       0x325
#define IDS_BAUD9600       0x326
#define IDS_BAUD14400      0x327
#define IDS_BAUD19200      0x328
#define IDS_BAUD38400      0x329
#define IDS_BAUD56000      0x32A
#define IDS_BAUD128000     0x32B
#define IDS_BAUD256000     0x32C

#define IDS_PARITYNONE     0x330
#define IDS_PARITYEVEN     0x331
#define IDS_PARITYODD      0x332
#define IDS_PARITYMARK     0x333
#define IDS_PARITYSPACE    0x334

#define IDS_ONESTOPBIT     0x340
#define IDS_ONE5STOPBITS   0x341
#define IDS_TWOSTOPBITS    0x342


TTY.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//---------------------------------------------------------------------------
//
//  Module: tty.c
//
//  Purpose:
//     The sample application demonstrates the usage of the COMM
//     API.  It implements the new COMM API of Windows 3.1.
//
//     NOTE:  no escape sequences are translated, only
//            the necessary control codes (LF, CR, BS, etc.)
//
//  Description of functions:
//     Descriptions are contained in the function headers.
//
//---------------------------------------------------------------------------
//
//  Written by Microsoft Product Support Services, Windows Developer Support.
//
//---------------------------------------------------------------------------

#include "tty.h"

//---------------------------------------------------------------------------
//  int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance,
//                      LPSTR lpszCmdLine, int nCmdShow )
//
//  Description:
//     This is the main window loop!
//
//  Parameters:
//     As documented for all WinMain() functions.
//
//---------------------------------------------------------------------------

int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow )
{
   MSG   msg ;

   if (!hPrevInstance)
      if (!InitApplication( hInstance ))
         return ( FALSE ) ;

   if (NULL == (hTTYWnd = InitInstance( hInstance, nCmdShow )))
      return ( FALSE ) ;

   while (GetMessage( &msg, NULL, 0, 0 ))
   {
      if (!TranslateAccelerator( hTTYWnd, ghAccel, &msg ))
      {
         TranslateMessage( &msg ) ;
         DispatchMessage( &msg ) ;
      }
   }
   return ( (int) msg.wParam ) ;

} // end of WinMain()

//---------------------------------------------------------------------------
//  BOOL NEAR InitApplication( HANDLE hInstance )
//
//  Description:
//     First time initialization stuff.  This registers information
//     such as window classes.
//
//  Parameters:
//     HANDLE hInstance
//        Handle to this instance of the application.
//
//---------------------------------------------------------------------------

BOOL NEAR InitApplication( HANDLE hInstance )
{
   WNDCLASS  wndclass ;

   // register tty window class

   wndclass.style =         0 ;
   wndclass.lpfnWndProc =   TTYWndProc ;
   wndclass.cbClsExtra =    0 ;
   wndclass.cbWndExtra =    TTYEXTRABYTES ;
   wndclass.hInstance =     hInstance ;
   wndclass.hIcon =         LoadIcon( hInstance, MAKEINTRESOURCE( TTYICON ) );
   wndclass.hCursor =       LoadCursor( NULL, IDC_ARROW ) ;
   wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1) ;
   wndclass.lpszMenuName =  MAKEINTRESOURCE( TTYMENU ) ;
   wndclass.lpszClassName = gszTTYClass ;

   return( RegisterClass( &wndclass ) ) ;

} // end of InitApplication()

//---------------------------------------------------------------------------
//  HWND NEAR InitInstance( HANDLE hInstance, int nCmdShow )
//
//  Description:
//     Initializes instance specific information.
//
//  Parameters:
//     HANDLE hInstance
//        Handle to instance
//
//     int nCmdShow
//        How do we show the window?
//
//---------------------------------------------------------------------------

HWND NEAR InitInstance( HANDLE hInstance, int nCmdShow )
{
   HWND  hTTYWnd ;

   // load accelerators
   ghAccel = LoadAccelerators( hInstance, MAKEINTRESOURCE( TTYACCEL ) ) ;

   // create the TTY window
   hTTYWnd = CreateWindow( gszTTYClass, gszAppName,
                           WS_OVERLAPPEDWINDOW,
                           CW_USEDEFAULT, CW_USEDEFAULT,
                           CW_USEDEFAULT, CW_USEDEFAULT,
                           NULL, NULL, hInstance, NULL ) ;

   if (NULL == hTTYWnd)
      return ( NULL ) ;

   ShowWindow( hTTYWnd, nCmdShow ) ;
   UpdateWindow( hTTYWnd ) ;

   return ( hTTYWnd ) ;

} // end of InitInstance()

//---------------------------------------------------------------------------
//  LRESULT FAR PASCAL TTYWndProc( HWND hWnd, UINT uMsg,
//                                 WPARAM wParam, LPARAM lParam )
//
//  Description:
//     This is the TTY Window Proc.  This handles ALL messages
//     to the tty window.
//
//  Parameters:
//     As documented for Window procedures.
//
//  Win-32 Porting Issues:
//     - WM_HSCROLL and WM_VSCROLL packing is different under Win-32.
//     - Needed LOWORD() of wParam for WM_CHAR messages.
//
//---------------------------------------------------------------------------

LRESULT FAR PASCAL TTYWndProc( HWND hWnd, UINT uMsg,
                               WPARAM wParam, LPARAM lParam )
{

   DWORD dwBlockSize=512;
   char * szBuffer;

   switch (uMsg)
   {

      case WM_CREATE:
         return ( CreateTTYInfo( hWnd ) ) ;

      case WM_COMMAND:
      {
         switch ( LOWORD( wParam ) )
         {
            case IDM_CONNECT:
               if (!OpenConnection( hWnd ))
                  MessageBox( hWnd, "Connection failed.", gszAppName,
                              MB_ICONEXCLAMATION ) ;
               break ;

            case IDM_DISCONNECT:
               KillTTYFocus( hWnd ) ;
               CloseConnection( hWnd ) ;
               break ;

            case IDM_SETTINGS:
            {
               NPTTYINFO  npTTYInfo ;

               if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
                  return ( FALSE ) ;
               GoModalDialogBoxParam( GETHINST( hWnd ),
                                      MAKEINTRESOURCE( SETTINGSDLGBOX ), hWnd,
                                      (DLGPROC) SettingsDlgProc,
                                      (LPARAM) (LPSTR) npTTYInfo ) ;

               // if fConnected, set new COM parameters

               if (CONNECTED( npTTYInfo ))
               {
                  if (!SetupConnection( hWnd ))
                     MessageBox( hWnd, "Settings failed!", gszAppName,
                                 MB_ICONEXCLAMATION ) ;
               }
            }
            break ;

            case IDM_SENDBLOCK:
               // This function is very useful to test how
               // your comm application handles large blocks
               // of data

               szBuffer = malloc(dwBlockSize);
               memset(szBuffer, 'X', dwBlockSize);
               WriteCommBlock( hWnd, szBuffer, dwBlockSize);
               free(szBuffer);

               break;

            case IDM_ABOUT:
               GoModalDialogBoxParam ( GETHINST( hWnd ),
                                       MAKEINTRESOURCE( ABOUTDLGBOX ),
                                       hWnd,
                                       (DLGPROC) AboutDlgProc, 0L ) ;
               break;

            case IDM_EXIT:
               PostMessage( hWnd, WM_CLOSE, 0, 0L ) ;
               break ;
         }
      }
      break ;

      case WM_PAINT:
         PaintTTY( hWnd ) ;
         break ;

      case WM_SIZE:
         SizeTTY( hWnd, HIWORD( lParam ), LOWORD( lParam ) ) ;
         break ;

      case WM_HSCROLL:
         ScrollTTYHorz( hWnd, LOWORD( wParam ), HIWORD( wParam ) ) ;
         break ;

      case WM_VSCROLL:
         ScrollTTYVert( hWnd, LOWORD( wParam ), HIWORD( wParam ) ) ;
         break ;

      case WM_CHAR:
         ProcessTTYCharacter( hWnd, LOBYTE( LOWORD( wParam ) ) ) ;
         break ;

      case WM_SETFOCUS:
         SetTTYFocus( hWnd ) ;
         break ;

      case WM_KILLFOCUS:
         KillTTYFocus( hWnd ) ;
         break ;

      case WM_DESTROY:
         DestroyTTYInfo( hWnd ) ;
         PostQuitMessage( 0 ) ;
         break ;

      case WM_CLOSE:
         if (IDOK != MessageBox( hWnd, "OK to close window?", "TTY Sample",
                                 MB_ICONQUESTION | MB_OKCANCEL ))
            break ;

         // fall through

      default:
         return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
   }
   return 0L ;

} // end of TTYWndProc()

//---------------------------------------------------------------------------
//  LRESULT NEAR CreateTTYInfo( HWND hWnd )
//
//  Description:
//     Creates the tty information structure and sets
//     menu option availability.  Returns -1 if unsuccessful.
//
//  Parameters:
//     HWND  hWnd
//        Handle to main window.
//
//  Win-32 Porting Issues:
//     - Needed to initialize TERMWND( npTTYInfo ) for secondary thread.
//     - Needed to create/initialize overlapped structures used in reads &
//       writes to COMM device.
//
//---------------------------------------------------------------------------

LRESULT NEAR CreateTTYInfo( HWND hWnd )
{
   HMENU       hMenu ;
   NPTTYINFO   npTTYInfo ;

   if (NULL == (npTTYInfo =
                   (NPTTYINFO) LocalAlloc( LPTR, sizeof( TTYINFO ) )))
      return ( (LRESULT) -1 ) ;

   // initialize TTY info structure

   COMDEV( npTTYInfo )        = 0 ;
   CONNECTED( npTTYInfo )     = FALSE ;
   CURSORSTATE( npTTYInfo )   = CS_HIDE ;
   LOCALECHO( npTTYInfo )     = FALSE ;
   AUTOWRAP( npTTYInfo )      = TRUE ;
   PORT( npTTYInfo )          = 1 ;
   BAUDRATE( npTTYInfo )      = CBR_9600 ;
   BYTESIZE( npTTYInfo )      = 8 ;
   //FLOWCTRL( npTTYInfo )      = FC_RTSCTS ;
   FLOWCTRL( npTTYInfo )      = FC_XONXOFF ;
   PARITY( npTTYInfo )        = NOPARITY ;
   STOPBITS( npTTYInfo )      = ONESTOPBIT ;
   XONXOFF( npTTYInfo )       = FALSE ;
   XSIZE( npTTYInfo )         = 0 ;
   YSIZE( npTTYInfo )         = 0 ;
   XSCROLL( npTTYInfo )       = 0 ;
   YSCROLL( npTTYInfo )       = 0 ;
   XOFFSET( npTTYInfo )       = 0 ;
   YOFFSET( npTTYInfo )       = 0 ;
   COLUMN( npTTYInfo )        = 0 ;
   ROW( npTTYInfo )           = 0 ;
   HTTYFONT( npTTYInfo )      = NULL ;
   FGCOLOR( npTTYInfo )       = RGB( 0, 0, 0 ) ;
   USECNRECEIVE( npTTYInfo )  = TRUE ;
   DISPLAYERRORS( npTTYInfo ) = TRUE ;
   WRITE_OS( npTTYInfo ).Offset = 0 ;
   WRITE_OS( npTTYInfo ).OffsetHigh = 0 ;
   READ_OS( npTTYInfo ).Offset = 0 ;
   READ_OS( npTTYInfo ).OffsetHigh = 0 ;
   TERMWND( npTTYInfo ) =       hWnd ;

   // create I/O event used for overlapped reads / writes

   READ_OS( npTTYInfo ).hEvent = CreateEvent( NULL,    // no security
                                              TRUE,    // explicit reset req
                                              FALSE,   // initial event reset
                                              NULL ) ; // no name
   if (READ_OS( npTTYInfo ).hEvent == NULL)
   {
      LocalFree( npTTYInfo ) ;
      return ( -1 ) ;
   }
   WRITE_OS( npTTYInfo ).hEvent = CreateEvent( NULL,    // no security
                                               TRUE,    // explicit reset req
                                               FALSE,   // initial event reset
                                               NULL ) ; // no name
   if (NULL == WRITE_OS( npTTYInfo ).hEvent)
   {
      CloseHandle( READ_OS( npTTYInfo ).hEvent ) ;
      LocalFree( npTTYInfo ) ;
      return ( -1 ) ;
   }

   // clear screen space

   _fmemset( SCREEN( npTTYInfo ), ' ', MAXROWS * MAXCOLS ) ;

   // setup default font information

   LFTTYFONT( npTTYInfo ).lfHeight =         9 ;
   LFTTYFONT( npTTYInfo ).lfWidth =          0 ;
   LFTTYFONT( npTTYInfo ).lfEscapement =     0 ;
   LFTTYFONT( npTTYInfo ).lfOrientation =    0 ;
   LFTTYFONT( npTTYInfo ).lfWeight =         0 ;
   LFTTYFONT( npTTYInfo ).lfItalic =         0 ;
   LFTTYFONT( npTTYInfo ).lfUnderline =      0 ;
   LFTTYFONT( npTTYInfo ).lfStrikeOut =      0 ;
   LFTTYFONT( npTTYInfo ).lfCharSet =        OEM_CHARSET ;
   LFTTYFONT( npTTYInfo ).lfOutPrecision =   OUT_DEFAULT_PRECIS ;
   LFTTYFONT( npTTYInfo ).lfClipPrecision =  CLIP_DEFAULT_PRECIS ;
   LFTTYFONT( npTTYInfo ).lfQuality =        DEFAULT_QUALITY ;
   LFTTYFONT( npTTYInfo ).lfPitchAndFamily = FIXED_PITCH | FF_MODERN ;
   lstrcpy( LFTTYFONT( npTTYInfo ).lfFaceName, "FixedSys" ) ;

   // set TTYInfo handle before any further message processing.

   SETNPTTYINFO( hWnd, npTTYInfo ) ;

   // reset the character information, etc.

   ResetTTYScreen( hWnd, npTTYInfo ) ;

   hMenu = GetMenu( hWnd ) ;
   EnableMenuItem( hMenu, IDM_DISCONNECT,
                   MF_GRAYED | MF_DISABLED | MF_BYCOMMAND ) ;
   EnableMenuItem( hMenu, IDM_CONNECT, MF_ENABLED | MF_BYCOMMAND ) ;

   return ( (LRESULT) TRUE ) ;

} // end of CreateTTYInfo()

//---------------------------------------------------------------------------
//  BOOL NEAR DestroyTTYInfo( HWND hWnd )
//
//  Description:
//     Destroys block associated with TTY window handle.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//  Win-32 Porting Issues:
//     - Needed to clean up event objects created during initialization.
//
//---------------------------------------------------------------------------

BOOL NEAR DestroyTTYInfo( HWND hWnd )
{
   NPTTYINFO npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   // force connection closed (if not already closed)

   if (CONNECTED( npTTYInfo ))
      CloseConnection( hWnd ) ;

   // clean up event objects

   CloseHandle( READ_OS( npTTYInfo ).hEvent ) ;
   CloseHandle( WRITE_OS( npTTYInfo ).hEvent ) ;
   CloseHandle( POSTEVENT( npTTYInfo ) ) ;

   DeleteObject( HTTYFONT( npTTYInfo ) ) ;

   LocalFree( npTTYInfo ) ;
   return ( TRUE ) ;

} // end of DestroyTTYInfo()

//---------------------------------------------------------------------------
//  BOOL NEAR ResetTTYScreen( HWND hWnd, NPTTYINFO npTTYInfo )
//
//  Description:
//     Resets the TTY character information and causes the
//     screen to resize to update the scroll information.
//
//  Parameters:
//     NPTTYINFO  npTTYInfo
//        pointer to TTY info structure
//
//---------------------------------------------------------------------------

BOOL NEAR ResetTTYScreen( HWND hWnd, NPTTYINFO npTTYInfo )
{
   HDC         hDC ;
   TEXTMETRIC  tm ;
   RECT        rcWindow ;

   if (NULL == npTTYInfo)
      return ( FALSE ) ;

   if (NULL != HTTYFONT( npTTYInfo ))
      DeleteObject( HTTYFONT( npTTYInfo ) ) ;

   HTTYFONT( npTTYInfo ) = CreateFontIndirect( &LFTTYFONT( npTTYInfo ) ) ;

   hDC = GetDC( hWnd ) ;
   SelectObject( hDC, HTTYFONT( npTTYInfo ) ) ;
   GetTextMetrics( hDC, &tm ) ;
   ReleaseDC( hWnd, hDC ) ;

   XCHAR( npTTYInfo ) = tm.tmAveCharWidth  ;
   YCHAR( npTTYInfo ) = tm.tmHeight + tm.tmExternalLeading ;

   // a slimy hack to force the scroll position, region to
   // be recalculated based on the new character sizes

   GetWindowRect( hWnd, &rcWindow ) ;
   SendMessage( hWnd, WM_SIZE, SIZENORMAL,
                (LPARAM) MAKELONG( rcWindow.right - rcWindow.left,
                                   rcWindow.bottom - rcWindow.top ) ) ;

   return ( TRUE ) ;

} // end of ResetTTYScreen()

//---------------------------------------------------------------------------
//  BOOL NEAR PaintTTY( HWND hWnd )
//
//  Description:
//     Paints the rectangle determined by the paint struct of
//     the DC.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window (as always)
//
//---------------------------------------------------------------------------

BOOL NEAR PaintTTY( HWND hWnd )
{
   int          nRow, nCol, nEndRow, nEndCol, nCount, nHorzPos, nVertPos ;
   HDC          hDC ;
   HFONT        hOldFont ;
   NPTTYINFO    npTTYInfo ;
   PAINTSTRUCT  ps ;
   RECT         rect ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   hDC = BeginPaint( hWnd, &ps ) ;
   hOldFont = SelectObject( hDC, HTTYFONT( npTTYInfo ) ) ;
   SetTextColor( hDC, FGCOLOR( npTTYInfo ) ) ;
   SetBkColor( hDC, GetSysColor( COLOR_WINDOW ) ) ;
   rect = ps.rcPaint ;
   nRow =
      min( MAXROWS - 1,
           max( 0, (rect.top + YOFFSET( npTTYInfo )) / YCHAR( npTTYInfo ) ) ) ;
   nEndRow =
      min( MAXROWS - 1,
           ((rect.bottom + YOFFSET( npTTYInfo ) - 1) / YCHAR( npTTYInfo ) ) ) ;
   nCol =
      min( MAXCOLS - 1,
           max( 0, (rect.left + XOFFSET( npTTYInfo )) / XCHAR( npTTYInfo ) ) ) ;
   nEndCol =
      min( MAXCOLS - 1,
           ((rect.right + XOFFSET( npTTYInfo ) - 1) / XCHAR( npTTYInfo ) ) ) ;
   nCount = nEndCol - nCol + 1 ;
   for (; nRow <= nEndRow; nRow++)
   {
      nVertPos = (nRow * YCHAR( npTTYInfo )) - YOFFSET( npTTYInfo ) ;
      nHorzPos = (nCol * XCHAR( npTTYInfo )) - XOFFSET( npTTYInfo ) ;
      rect.top = nVertPos ;
      rect.bottom = nVertPos + YCHAR( npTTYInfo ) ;
      rect.left = nHorzPos ;
      rect.right = nHorzPos + XCHAR( npTTYInfo ) * nCount ;
      SetBkMode( hDC, OPAQUE ) ;
      ExtTextOut( hDC, nHorzPos, nVertPos, ETO_OPAQUE | ETO_CLIPPED, &rect,
                  (LPSTR)( SCREEN( npTTYInfo ) + nRow * MAXCOLS + nCol ),
                  nCount, NULL ) ;
   }
   SelectObject( hDC, hOldFont ) ;
   EndPaint( hWnd, &ps ) ;
   MoveTTYCursor( hWnd ) ;
   return ( TRUE ) ;

} // end of PaintTTY()

//---------------------------------------------------------------------------
//  BOOL NEAR SizeTTY( HWND hWnd, WORD wVertSize, WORD wHorzSize )
//
//  Description:
//     Sizes TTY and sets up scrolling regions.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//     WORD wVertSize
//        new vertical size
//
//     WORD wHorzSize
//        new horizontal size
//
//---------------------------------------------------------------------------

BOOL NEAR SizeTTY( HWND hWnd, WORD wVertSize, WORD wHorzSize )
{
   int        nScrollAmt ;
   NPTTYINFO  npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   YSIZE( npTTYInfo ) = (int) wVertSize ;
   YSCROLL( npTTYInfo ) = max( 0, (MAXROWS * YCHAR( npTTYInfo )) -
                               YSIZE( npTTYInfo ) ) ;
   nScrollAmt = min( YSCROLL( npTTYInfo ), YOFFSET( npTTYInfo ) ) -
                     YOFFSET( npTTYInfo ) ;
   ScrollWindow( hWnd, 0, -nScrollAmt, NULL, NULL ) ;

   YOFFSET( npTTYInfo ) = YOFFSET( npTTYInfo ) + nScrollAmt ;
   SetScrollPos( hWnd, SB_VERT, YOFFSET( npTTYInfo ), FALSE ) ;
   SetScrollRange( hWnd, SB_VERT, 0, YSCROLL( npTTYInfo ), TRUE ) ;

   XSIZE( npTTYInfo ) = (int) wHorzSize ;
   XSCROLL( npTTYInfo ) = max( 0, (MAXCOLS * XCHAR( npTTYInfo )) -
                                XSIZE( npTTYInfo ) ) ;
   nScrollAmt = min( XSCROLL( npTTYInfo ), XOFFSET( npTTYInfo )) -
                     XOFFSET( npTTYInfo ) ;
   ScrollWindow( hWnd, 0, -nScrollAmt, NULL, NULL ) ;
   XOFFSET( npTTYInfo ) = XOFFSET( npTTYInfo ) + nScrollAmt ;
   SetScrollPos( hWnd, SB_HORZ, XOFFSET( npTTYInfo ), FALSE ) ;
   SetScrollRange( hWnd, SB_HORZ, 0, XSCROLL( npTTYInfo ), TRUE ) ;

   InvalidateRect( hWnd, NULL, TRUE ) ;

   return ( TRUE ) ;

} // end of SizeTTY()

//---------------------------------------------------------------------------
//  BOOL NEAR ScrollTTYVert( HWND hWnd, WORD wScrollCmd, WORD wScrollPos )
//
//  Description:
//     Scrolls TTY window vertically.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//     WORD wScrollCmd
//        type of scrolling we're doing
//
//     WORD wScrollPos
//        scroll position
//
//---------------------------------------------------------------------------

BOOL NEAR ScrollTTYVert( HWND hWnd, WORD wScrollCmd, WORD wScrollPos )
{
   int        nScrollAmt ;
   NPTTYINFO  npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   switch (wScrollCmd)
   {
      case SB_TOP:
         nScrollAmt = -YOFFSET( npTTYInfo ) ;
         break ;

      case SB_BOTTOM:
         nScrollAmt = YSCROLL( npTTYInfo ) - YOFFSET( npTTYInfo ) ;
         break ;

      case SB_PAGEUP:
         nScrollAmt = -YSIZE( npTTYInfo ) ;
         break ;

      case SB_PAGEDOWN:
         nScrollAmt = YSIZE( npTTYInfo ) ;
         break ;

      case SB_LINEUP:
         nScrollAmt = -YCHAR( npTTYInfo ) ;
         break ;

      case SB_LINEDOWN:
         nScrollAmt = YCHAR( npTTYInfo ) ;
         break ;

      case SB_THUMBPOSITION:
         nScrollAmt = wScrollPos - YOFFSET( npTTYInfo ) ;
         break ;

      default:
         return ( FALSE ) ;
   }
   if ((YOFFSET( npTTYInfo ) + nScrollAmt) > YSCROLL( npTTYInfo ))
      nScrollAmt = YSCROLL( npTTYInfo ) - YOFFSET( npTTYInfo ) ;
   if ((YOFFSET( npTTYInfo ) + nScrollAmt) < 0)
      nScrollAmt = -YOFFSET( npTTYInfo ) ;
   ScrollWindow( hWnd, 0, -nScrollAmt, NULL, NULL ) ;
   YOFFSET( npTTYInfo ) = YOFFSET( npTTYInfo ) + nScrollAmt ;
   SetScrollPos( hWnd, SB_VERT, YOFFSET( npTTYInfo ), TRUE ) ;

   return ( TRUE ) ;

} // end of ScrollTTYVert()

//---------------------------------------------------------------------------
//  BOOL NEAR ScrollTTYHorz( HWND hWnd, WORD wScrollCmd, WORD wScrollPos )
//
//  Description:
//     Scrolls TTY window horizontally.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//     WORD wScrollCmd
//        type of scrolling we're doing
//
//     WORD wScrollPos
//        scroll position
//
//---------------------------------------------------------------------------

BOOL NEAR ScrollTTYHorz( HWND hWnd, WORD wScrollCmd, WORD wScrollPos )
{
   int        nScrollAmt ;
   NPTTYINFO  npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   switch (wScrollCmd)
   {
      case SB_TOP:
         nScrollAmt = -XOFFSET( npTTYInfo ) ;
         break ;

      case SB_BOTTOM:
         nScrollAmt = XSCROLL( npTTYInfo ) - XOFFSET( npTTYInfo ) ;
         break ;

      case SB_PAGEUP:
         nScrollAmt = -XSIZE( npTTYInfo ) ;
         break ;

      case SB_PAGEDOWN:
         nScrollAmt = XSIZE( npTTYInfo ) ;
         break ;

      case SB_LINEUP:
         nScrollAmt = -XCHAR( npTTYInfo ) ;
         break ;

      case SB_LINEDOWN:
         nScrollAmt = XCHAR( npTTYInfo ) ;
         break ;

      case SB_THUMBPOSITION:
         nScrollAmt = wScrollPos - XOFFSET( npTTYInfo ) ;
         break ;

      default:
         return ( FALSE ) ;
   }
   if ((XOFFSET( npTTYInfo ) + nScrollAmt) > XSCROLL( npTTYInfo ))
      nScrollAmt = XSCROLL( npTTYInfo ) - XOFFSET( npTTYInfo ) ;
   if ((XOFFSET( npTTYInfo ) + nScrollAmt) < 0)
      nScrollAmt = -XOFFSET( npTTYInfo ) ;
   ScrollWindow( hWnd, -nScrollAmt, 0, NULL, NULL ) ;
   XOFFSET( npTTYInfo ) = XOFFSET( npTTYInfo ) + nScrollAmt ;
   SetScrollPos( hWnd, SB_HORZ, XOFFSET( npTTYInfo ), TRUE ) ;

   return ( TRUE ) ;

} // end of ScrollTTYHorz()

//---------------------------------------------------------------------------
//  BOOL NEAR SetTTYFocus( HWND hWnd )
//
//  Description:
//     Sets the focus to the TTY window also creates caret.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//---------------------------------------------------------------------------

BOOL NEAR SetTTYFocus( HWND hWnd )
{
   NPTTYINFO  npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   if (CONNECTED( npTTYInfo ) && (CURSORSTATE( npTTYInfo ) != CS_SHOW))
   {
      CreateCaret( hWnd, NULL, XCHAR( npTTYInfo ), YCHAR( npTTYInfo ) ) ;
      ShowCaret( hWnd ) ;
      CURSORSTATE( npTTYInfo ) = CS_SHOW ;
   }
   MoveTTYCursor( hWnd ) ;
   return ( TRUE ) ;

} // end of SetTTYFocus()

//---------------------------------------------------------------------------
//  BOOL NEAR KillTTYFocus( HWND hWnd )
//
//  Description:
//     Kills TTY focus and destroys the caret.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//---------------------------------------------------------------------------

BOOL NEAR KillTTYFocus( HWND hWnd )
{
   NPTTYINFO  npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   if (CONNECTED( npTTYInfo ) && (CURSORSTATE( npTTYInfo ) != CS_HIDE))
   {
      HideCaret( hWnd ) ;
      DestroyCaret() ;
      CURSORSTATE( npTTYInfo ) = CS_HIDE ;
   }
   return ( TRUE ) ;

} // end of KillTTYFocus()

//---------------------------------------------------------------------------
//  BOOL NEAR MoveTTYCursor( HWND hWnd )
//
//  Description:
//     Moves caret to current position.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//---------------------------------------------------------------------------

BOOL NEAR MoveTTYCursor( HWND hWnd )
{
   NPTTYINFO  npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   if (CONNECTED( npTTYInfo ) && (CURSORSTATE( npTTYInfo ) & CS_SHOW))
      SetCaretPos( (COLUMN( npTTYInfo ) * XCHAR( npTTYInfo )) -
                   XOFFSET( npTTYInfo ),
                   (ROW( npTTYInfo ) * YCHAR( npTTYInfo )) -
                   YOFFSET( npTTYInfo ) ) ;

   return ( TRUE ) ;

} // end of MoveTTYCursor()

//---------------------------------------------------------------------------
//  BOOL NEAR ProcessTTYCharacter( HWND hWnd, BYTE bOut )
//
//  Description:
//     This simply writes a character to the port and echos it
//     to the TTY screen if fLocalEcho is set.  Some minor
//     keyboard mapping could be performed here.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//     BYTE bOut
//        byte from keyboard
//
//---------------------------------------------------------------------------

BOOL NEAR ProcessTTYCharacter( HWND hWnd, BYTE bOut )
{
   NPTTYINFO  npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   if (!CONNECTED( npTTYInfo ))
      return ( FALSE ) ;

   // a robust app would take appropriate steps if WriteCommBlock failed
   WriteCommBlock( hWnd, &bOut, 1 ) ;
   if (LOCALECHO( npTTYInfo ))
      WriteTTYBlock( hWnd, &bOut, 1 ) ;

   return ( TRUE ) ;

} // end of ProcessTTYCharacter()

//---------------------------------------------------------------------------
//  BOOL NEAR OpenConnection( HWND hWnd )
//
//  Description:
//     Opens communication port specified in the TTYINFO struct.
//     It also sets the CommState and notifies the window via
//     the fConnected flag in the TTYINFO struct.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//  Win-32 Porting Issues:
//     - OpenComm() is not supported under Win-32.  Use CreateFile()
//       and setup for OVERLAPPED_IO.
//     - Win-32 has specific communication timeout parameters.
//     - Created the secondary thread for event notification.
//
//---------------------------------------------------------------------------

BOOL NEAR OpenConnection( HWND hWnd )
{
   char       szPort[ 15 ], szTemp[ 10 ] ;
   BOOL       fRetVal ;
   HCURSOR    hOldCursor, hWaitCursor ;
   HMENU      hMenu ;
   NPTTYINFO  npTTYInfo ;

   HANDLE        hCommWatchThread ;
   DWORD         dwThreadID ;
   COMMTIMEOUTS  CommTimeOuts ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   // show the hourglass cursor
   hWaitCursor = LoadCursor( NULL, IDC_WAIT ) ;
   hOldCursor = SetCursor( hWaitCursor ) ;

   // load the COM prefix string and append port number

   LoadString( GETHINST( hWnd ), IDS_COMPREFIX, szTemp, sizeof( szTemp ) ) ;
   wsprintf( szPort, "%s%d", (LPSTR) szTemp, PORT( npTTYInfo ) ) ;

   // open COMM device

   if ((COMDEV( npTTYInfo ) =
      CreateFile( szPort, GENERIC_READ | GENERIC_WRITE,
                  0,                    // exclusive access
                  NULL,                 // no security attrs
                  OPEN_EXISTING,
                  FILE_ATTRIBUTE_NORMAL |
                  FILE_FLAG_OVERLAPPED, // overlapped I/O
                  NULL )) == (HANDLE) -1 )
      return ( FALSE ) ;
   else
   {
      // get any early notifications

      SetCommMask( COMDEV( npTTYInfo ), EV_RXCHAR ) ;

      // setup device buffers

      SetupComm( COMDEV( npTTYInfo ), 4096, 4096 ) ;

      // purge any information in the buffer

      PurgeComm( COMDEV( npTTYInfo ), PURGE_TXABORT | PURGE_RXABORT |

PURGE_TXCLEAR | PURGE_RXCLEAR ) ;

      // set up for overlapped I/O

      CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF ;
      CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;
      CommTimeOuts.ReadTotalTimeoutConstant = 1000 ;
      // CBR_9600 is approximately 1byte/ms. For our purposes, allow
      // double the expected time per character for a fudge factor.
      CommTimeOuts.WriteTotalTimeoutMultiplier = 2*CBR_9600/BAUDRATE( npTTYInfo ) ;
      CommTimeOuts.WriteTotalTimeoutConstant = 0 ;
      SetCommTimeouts( COMDEV( npTTYInfo ), &CommTimeOuts ) ;
   }

   fRetVal = SetupConnection( hWnd ) ;

   if (fRetVal)
   {
      CONNECTED( npTTYInfo ) = TRUE ;

      // Create a secondary thread
      // to watch for an event.

      if (NULL == (hCommWatchThread =
                      CreateThread( (LPSECURITY_ATTRIBUTES) NULL,
                                    0,
                                    (LPTHREAD_START_ROUTINE) CommWatchProc,
                                    (LPVOID) npTTYInfo,
                                    0, &dwThreadID )))
      {
         CONNECTED( npTTYInfo ) = FALSE ;
         CloseHandle( COMDEV( npTTYInfo ) ) ;
         fRetVal = FALSE ;
      }
      else
      {
         THREADID( npTTYInfo ) = dwThreadID ;
         HTHREAD( npTTYInfo ) = hCommWatchThread ;

         // assert DTR

         EscapeCommFunction( COMDEV( npTTYInfo ), SETDTR ) ;

         SetTTYFocus( hWnd ) ;

         hMenu = GetMenu( hWnd ) ;
         EnableMenuItem( hMenu, IDM_DISCONNECT,
                        MF_ENABLED | MF_BYCOMMAND ) ;
         EnableMenuItem( hMenu, IDM_CONNECT,
                        MF_GRAYED | MF_DISABLED | MF_BYCOMMAND ) ;

      }
   }
   else
   {
      CONNECTED( npTTYInfo ) = FALSE ;
      CloseHandle( COMDEV( npTTYInfo ) ) ;
   }

   // restore cursor

   SetCursor( hOldCursor ) ;

   return ( fRetVal ) ;

} // end of OpenConnection()

//---------------------------------------------------------------------------
//  BOOL NEAR SetupConnection( HWND hWnd )
//
//  Description:
//     This routines sets up the DCB based on settings in the
//     TTY info structure and performs a SetCommState().
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//  Win-32 Porting Issues:
//     - Win-32 requires a slightly different processing of the DCB.
//       Changes were made for configuration of the hardware handshaking
//       lines.
//
//---------------------------------------------------------------------------

BOOL NEAR SetupConnection( HWND hWnd )
{
   BOOL       fRetVal ;
   BYTE       bSet ;
   DCB        dcb ;
   NPTTYINFO  npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   dcb.DCBlength = sizeof( DCB ) ;

   GetCommState( COMDEV( npTTYInfo ), &dcb ) ;

   dcb.BaudRate = BAUDRATE( npTTYInfo ) ;
   dcb.ByteSize = BYTESIZE( npTTYInfo ) ;
   dcb.Parity = PARITY( npTTYInfo ) ;
   dcb.StopBits = STOPBITS( npTTYInfo ) ;

   // setup hardware flow control

   bSet = (BYTE) ((FLOWCTRL( npTTYInfo ) & FC_DTRDSR) != 0) ;
   dcb.fOutxDsrFlow = bSet ;
   if (bSet)
      dcb.fDtrControl = DTR_CONTROL_HANDSHAKE ;
   else
      dcb.fDtrControl = DTR_CONTROL_ENABLE ;

   bSet = (BYTE) ((FLOWCTRL( npTTYInfo ) & FC_RTSCTS) != 0) ;
dcb.fOutxCtsFlow = bSet ;
   if (bSet)
      dcb.fRtsControl = RTS_CONTROL_HANDSHAKE ;
   else
      dcb.fRtsControl = RTS_CONTROL_ENABLE ;

   // setup software flow control

   bSet = (BYTE) ((FLOWCTRL( npTTYInfo ) & FC_XONXOFF) != 0) ;

   dcb.fInX = dcb.fOutX = bSet ;
   dcb.XonChar = ASCII_XON ;
   dcb.XoffChar = ASCII_XOFF ;
   dcb.XonLim = 100 ;
   dcb.XoffLim = 100 ;

   // other various settings

   dcb.fBinary = TRUE ;
   dcb.fParity = TRUE ;

   fRetVal = SetCommState( COMDEV( npTTYInfo ), &dcb ) ;

   return ( fRetVal ) ;

} // end of SetupConnection()

//---------------------------------------------------------------------------
//  BOOL NEAR CloseConnection( HWND hWnd )
//
//  Description:
//     Closes the connection to the port.  Resets the connect flag
//     in the TTYINFO struct.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//  Win-32 Porting Issues:
//     - Needed to stop secondary thread.  SetCommMask() will signal the
//       WaitCommEvent() event and the thread will halt when the
//       CONNECTED() flag is clear.
//     - Use new PurgeComm() API to clear communications driver before
//       closing device.
//
//---------------------------------------------------------------------------

BOOL NEAR CloseConnection( HWND hWnd )
{
   HMENU      hMenu ;
   NPTTYINFO  npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   // set connected flag to FALSE

   CONNECTED( npTTYInfo ) = FALSE ;

   // disable event notification and wait for thread
   // to halt

   SetCommMask( COMDEV( npTTYInfo ), 0 ) ;

   // block until thread has been halted

   while(THREADID(npTTYInfo) != 0);

   // kill the focus

   KillTTYFocus( hWnd ) ;

   // drop DTR

   EscapeCommFunction( COMDEV( npTTYInfo ), CLRDTR ) ;

   // purge any outstanding reads/writes and close device handle

   PurgeComm( COMDEV( npTTYInfo ), PURGE_TXABORT | PURGE_RXABORT |
                                   PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
   CloseHandle( COMDEV( npTTYInfo ) ) ;

   // change the selectable items in the menu

   hMenu = GetMenu( hWnd ) ;
   EnableMenuItem( hMenu, IDM_DISCONNECT,
                   MF_GRAYED | MF_DISABLED | MF_BYCOMMAND ) ;
   EnableMenuItem( hMenu, IDM_CONNECT,
                   MF_ENABLED | MF_BYCOMMAND ) ;

   return ( TRUE ) ;

} // end of CloseConnection()

//---------------------------------------------------------------------------
//  int NEAR ReadCommBlock( HWND hWnd, LPSTR lpszBlock, int nMaxLength )
//
//  Description:
//     Reads a block from the COM port and stuffs it into
//     the provided buffer.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//     LPSTR lpszBlock
//        block used for storage
//
//     int nMaxLength
//        max length of block to read
//
//  Win-32 Porting Issues:
//     - ReadComm() has been replaced by ReadFile() in Win-32.
//     - Overlapped I/O has been implemented.
//
//---------------------------------------------------------------------------

int NEAR ReadCommBlock( HWND hWnd, LPSTR lpszBlock, int nMaxLength )
{
   BOOL       fReadStat ;
   COMSTAT    ComStat ;
   DWORD      dwErrorFlags;
   DWORD      dwLength;
   DWORD      dwError;
   char       szError[ 10 ] ;
   NPTTYINFO  npTTYInfo ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   // only try to read number of bytes in queue
   ClearCommError( COMDEV( npTTYInfo ), &dwErrorFlags, &ComStat ) ;
   dwLength = min( (DWORD) nMaxLength, ComStat.cbInQue ) ;

   if (dwLength > 0)
   {
      fReadStat = ReadFile( COMDEV( npTTYInfo ), lpszBlock,
                    dwLength, &dwLength, &READ_OS( npTTYInfo ) ) ;
      if (!fReadStat)
      {
         if (GetLastError() == ERROR_IO_PENDING)
         {
            OutputDebugString("\n\rIO Pending");
            // We have to wait for read to complete.
            // This function will timeout according to the
            // CommTimeOuts.ReadTotalTimeoutConstant variable
            // Every time it times out, check for port errors
            while(!GetOverlappedResult( COMDEV( npTTYInfo ),
               &READ_OS( npTTYInfo ), &dwLength, TRUE ))
            {
               dwError = GetLastError();
               if(dwError == ERROR_IO_INCOMPLETE)
                  // normal result if not finished
                  continue;
               else
               {
                  // an error occurred, try to recover
                  wsprintf( szError, "<CE-%u>", dwError ) ;
                  WriteTTYBlock( hWnd, szError, lstrlen( szError ) ) ;
                  ClearCommError( COMDEV( npTTYInfo ), &dwErrorFlags, &ComStat ) ;
                  if ((dwErrorFlags > 0) && DISPLAYERRORS( npTTYInfo ))
                  {
                  wsprintf( szError, "<CE-%u>", dwErrorFlags ) ;
                  WriteTTYBlock( hWnd, szError, lstrlen( szError ) ) ;
                  }
                  break;
               }

            }

      }
         else
         {
            // some other error occurred
            dwLength = 0 ;
            ClearCommError( COMDEV( npTTYInfo ), &dwErrorFlags, &ComStat ) ;
            if ((dwErrorFlags > 0) && DISPLAYERRORS( npTTYInfo ))
            {
            wsprintf( szError, "<CE-%u>", dwErrorFlags ) ;
            WriteTTYBlock( hWnd, szError, lstrlen( szError ) ) ;
            }
         }
      }
   }

   return ( dwLength ) ;

} // end of ReadCommBlock()

//---------------------------------------------------------------------------
//  BOOL NEAR WriteCommBlock( HWND hWnd, BYTE *pByte )
//
//  Description:
//     Writes a block of data to the COM port specified in the associated
//     TTY info structure.
//
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//     BYTE *pByte
//        pointer to data to write to port
//
//  Win-32 Porting Issues:
//     - WriteComm() has been replaced by WriteFile() in Win-32.
//     - Overlapped I/O has been implemented.
//
//---------------------------------------------------------------------------

BOOL NEAR WriteCommBlock( HWND hWnd, LPSTR lpByte , DWORD dwBytesToWrite)
{

   BOOL        fWriteStat ;
   DWORD       dwBytesWritten ;
   NPTTYINFO   npTTYInfo ;
   DWORD       dwErrorFlags;
   DWORD   dwError;
   DWORD       dwBytesSent=0;
   COMSTAT     ComStat;
   char        szError[ 128 ] ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   fWriteStat = WriteFile( COMDEV( npTTYInfo ), lpByte, dwBytesToWrite,
                           &dwBytesWritten, &WRITE_OS( npTTYInfo ) ) ;

   // Note that normally the code will not execute the following
   // because the driver caches write operations. Small I/O requests
   // (up to several thousand bytes) will normally be accepted
   // immediately and WriteFile will return true even though an
   // overlapped operation was specified

   if (!fWriteStat)
   {
      if(GetLastError() == ERROR_IO_PENDING)
      {
         // We should wait for the completion of the write operation
         // so we know if it worked or not

         // This is only one way to do this. It might be beneficial to
         // place the write operation in a separate thread
         // so that blocking on completion will not negatively
         // affect the responsiveness of the UI

         // If the write takes too long to complete, this
         // function will timeout according to the
         // CommTimeOuts.WriteTotalTimeoutMultiplier variable.
         // This code logs the timeout but does not retry
         // the write.

         while(!GetOverlappedResult( COMDEV( npTTYInfo ),
            &WRITE_OS( npTTYInfo ), &dwBytesWritten, TRUE ))
         {
            dwError = GetLastError();
            if(dwError == ERROR_IO_INCOMPLETE)
            {
               // normal result if not finished
               dwBytesSent += dwBytesWritten;
               continue;
            }
            else
            {
               // an error occurred, try to recover
               wsprintf( szError, "<CE-%u>", dwError ) ;
               WriteTTYBlock( hWnd, szError, lstrlen( szError ) ) ;
               ClearCommError( COMDEV( npTTYInfo ), &dwErrorFlags, &ComStat ) ;
               if ((dwErrorFlags > 0) && DISPLAYERRORS( npTTYInfo ))
               {
                  wsprintf( szError, "<CE-%u>", dwErrorFlags ) ;
                  WriteTTYBlock( hWnd, szError, lstrlen( szError ) ) ;
               }
               break;
            }
         }

         dwBytesSent += dwBytesWritten;

         if( dwBytesSent != dwBytesToWrite )
             wsprintf(szError,"\nProbable Write Timeout: Total of %ld bytes sent", dwBytesSent);
         else
             wsprintf(szError,"\n%ld bytes written", dwBytesSent);

         OutputDebugString(szError);

      }
      else
      {
         // some other error occurred
         ClearCommError( COMDEV( npTTYInfo ), &dwErrorFlags, &ComStat ) ;
         if ((dwErrorFlags > 0) && DISPLAYERRORS( npTTYInfo ))
         {
            wsprintf( szError, "<CE-%u>", dwErrorFlags ) ;
            WriteTTYBlock( hWnd, szError, lstrlen( szError ) ) ;
         }
         return ( FALSE );
      }
   }
   return ( TRUE ) ;

} // end of WriteCommBlock()

//---------------------------------------------------------------------------
//  BOOL NEAR WriteTTYBlock( HWND hWnd, LPSTR lpBlock, int nLength )
//
//  Description:
//     Writes block to TTY screen.  Nothing fancy - just
//     straight TTY.
// gl
//  Parameters:
//     HWND hWnd
//        handle to TTY window
//
//     LPSTR lpBlock
//        far pointer to block of data
//
//     int nLength
//        length of block
//
//---------------------------------------------------------------------------

BOOL NEAR WriteTTYBlock( HWND hWnd, LPSTR lpBlock, int nLength )
{
   int        i ;
   NPTTYINFO  npTTYInfo ;
   RECT       rect ;

   if (NULL == (npTTYInfo = GETNPTTYINFO( hWnd )))
      return ( FALSE ) ;

   for (i = 0 ; i < nLength; i++)
   {
      switch (lpBlock[ i ])
      {
         case ASCII_BEL:
            // Bell
            MessageBeep( 0 ) ;
            break ;

         case ASCII_BS:
            // Backspace
            if (COLUMN( npTTYInfo ) > 0)
               COLUMN( npTTYInfo ) -- ;
            MoveTTYCursor( hWnd ) ;
            break ;

         case ASCII_CR:
            // Carriage return
            COLUMN( npTTYInfo ) = 0 ;
            MoveTTYCursor( hWnd ) ;
            if (!NEWLINE( npTTYInfo ))
               break;

            // fall through

         case ASCII_LF:
            // Line feed
            if (ROW( npTTYInfo )++ == MAXROWS - 1)
            {
               _fmemmove( (LPSTR) (SCREEN( npTTYInfo )),
                          (LPSTR) (SCREEN( npTTYInfo ) + MAXCOLS),
                          (MAXROWS - 1) * MAXCOLS ) ;
               _fmemset( (LPSTR) (SCREEN( npTTYInfo ) + (MAXROWS - 1) * MAXCOLS),
                         ' ', MAXCOLS ) ;
               InvalidateRect( hWnd, NULL, FALSE ) ;
               ROW( npTTYInfo )-- ;
            }
            MoveTTYCursor( hWnd ) ;
            break ;

         default:
            *(SCREEN( npTTYInfo ) + ROW( npTTYInfo ) * MAXCOLS +
                COLUMN( npTTYInfo )) = lpBlock[ i ] ;
            rect.left = (COLUMN( npTTYInfo ) * XCHAR( npTTYInfo )) -
                        XOFFSET( npTTYInfo ) ;
            rect.right = rect.left + XCHAR( npTTYInfo ) ;
            rect.top = (ROW( npTTYInfo ) * YCHAR( npTTYInfo )) -
                       YOFFSET( npTTYInfo ) ;
            rect.bottom = rect.top + YCHAR( npTTYInfo ) ;
            InvalidateRect( hWnd, &rect, FALSE ) ;

            // Line wrap
            if (COLUMN( npTTYInfo ) < MAXCOLS - 1)
               COLUMN( npTTYInfo )++ ;
            else if (AUTOWRAP( npTTYInfo ))
               WriteTTYBlock( hWnd, "\r\n", 2 ) ;
            break;
      }
   }
   return ( TRUE ) ;

} // end of WriteTTYBlock()

//---------------------------------------------------------------------------
//  VOID NEAR GoModalDialogBoxParam( HINSTANCE hInstance,
//                                   LPCSTR lpszTemplate, HWND hWnd,
//                                   DLGPROC lpDlgProc, LPARAM lParam )
//
//  Description:
//     It is a simple utility function that simply performs the
//     MPI and invokes the dialog box with a DWORD paramter.
//
//  Parameters:
//     similar to that of DialogBoxParam() with the exception
//     that the lpDlgProc is not a procedure instance
//
//---------------------------------------------------------------------------

VOID NEAR GoModalDialogBoxParam( HINSTANCE hInstance, LPCSTR lpszTemplate,
                                 HWND hWnd, DLGPROC lpDlgProc, LPARAM lParam )
{
   DLGPROC  lpProcInstance ;

   lpProcInstance = (DLGPROC) MakeProcInstance( (FARPROC) lpDlgProc,
                                                hInstance ) ;
   DialogBoxParam( hInstance, lpszTemplate, hWnd, lpProcInstance, lParam ) ;
   FreeProcInstance( (FARPROC) lpProcInstance ) ;

} // end of GoModalDialogBoxParam()

//---------------------------------------------------------------------------
//  BOOL FAR PASCAL AboutDlgProc( HWND hDlg, UINT uMsg,
//                                WPARAM wParam, LPARAM lParam )
//
//  Description:
//     Simulates the Windows System Dialog Box.
//
//  Parameters:
//     Same as standard dialog procedures.
//
//---------------------------------------------------------------------------

BOOL FAR PASCAL AboutDlgProc( HWND hDlg, UINT uMsg,
                              WPARAM wParam, LPARAM lParam )
{
   switch (uMsg)
   {
      case WM_INITDIALOG:
      {
         char         szBuffer[ MAXLEN_TEMPSTR ], szTemp[ MAXLEN_TEMPSTR ];
         WORD         wRevision, wVersion ;

#ifdef ABOUTDLG_USEBITMAP
         // if we are using the bitmap, hide the icon

         ShowWindow( GetDlgItem( hDlg, IDD_ABOUTICON ), SW_HIDE ) ;
#endif
         // sets up the version number for Windows

         wVersion = LOWORD( GetVersion() ) ;
         wRevision = HIBYTE( wVersion ) ;
         wVersion = LOBYTE( wVersion ) ;

         GetDlgItemText( hDlg, IDD_TITLELINE, szTemp, sizeof( szTemp ) ) ;
         wsprintf( szBuffer, szTemp, wVersion, wRevision ) ;
         SetDlgItemText( hDlg, IDD_TITLELINE, szBuffer ) ;

         // sets up version number for TTY

         GetDlgItemText( hDlg, IDD_VERSION, szTemp, sizeof( szTemp ) ) ;
         wsprintf( szBuffer, szTemp, VER_MAJOR, VER_MINOR, VER_BUILD ) ;
         SetDlgItemText( hDlg, IDD_VERSION, (LPSTR) szBuffer ) ;

         // get by-line

         LoadString( GETHINST( hDlg ), IDS_BYLINE, szBuffer,
                     sizeof( szBuffer ) ) ;
         SetDlgItemText( hDlg, IDD_BYLINE, szBuffer ) ;

         SetDlgItemText( hDlg, IDD_WINDOWSMODE, "NT Mode" ) ;

      }
      return ( TRUE ) ;

#ifdef ABOUTDLG_USEBITMAP
      // used to paint the bitmap

      case WM_PAINT:
      {
         HBITMAP      hBitMap ;
         HDC          hDC, hMemDC ;
         PAINTSTRUCT  ps ;

         // load bitmap and display it

         hDC = BeginPaint( hDlg, &ps ) ;
         if (NULL != (hMemDC = CreateCompatibleDC( hDC )))
         {
            hBitMap = LoadBitmap( GETHINST( hDlg ),
                                  MAKEINTRESOURCE( TTYBITMAP ) ) ;
            hBitMap = SelectObject( hMemDC, hBitMap ) ;
            BitBlt( hDC, 10, 10, 64, 64, hMemDC, 0, 0, SRCCOPY ) ;
            DeleteObject( SelectObject( hMemDC, hBitMap ) ) ;
            DeleteDC( hMemDC ) ;
         }
         EndPaint( hDlg, &ps ) ;
      }
      break ;
#endif

      case WM_COMMAND:
         if (LOWORD( wParam ) == IDD_OK)
         {
            EndDialog( hDlg, TRUE ) ;
            return ( TRUE ) ;
         }
         break;
   }
   return ( FALSE ) ;

} // end of AboutDlgProc()

//---------------------------------------------------------------------------
//  VOID NEAR FillComboBox( HINSTANCE hInstance, HWND hCtrlWnd, int nIDString,
//                          WORD NEAR *npTable, WORD wTableLen,
//                          WORD wCurrentSetting )
//
//  Description:
//     Fills the given combo box with strings from the resource
//     table starting at nIDString.  Associated items are
//     added from given table.  The combo box is notified of
//     the current setting.
//
//  Parameters:
//     HINSTANCE hInstance
//        handle to application instance
//
//     HWND hCtrlWnd
//        handle to combo box control
//
//     int nIDString
//        first resource string id
//
//     DWORD NEAR *npTable
//        near point to table of associated values
//
//     WORD wTableLen
//        length of table
//
//     DWORD dwCurrentSetting
//        current setting (for combo box selection)
//
//---------------------------------------------------------------------------

VOID NEAR FillComboBox( HINSTANCE hInstance, HWND hCtrlWnd, int nIDString,
                        DWORD NEAR *npTable, WORD wTableLen,
                        DWORD dwCurrentSetting )
{
   char  szBuffer[ MAXLEN_TEMPSTR ] ;
   WORD  wCount, wPosition ;

   for (wCount = 0; wCount < wTableLen; wCount++)
   {
      // load the string from the string resources and
      // add it to the combo box

      LoadString( hInstance, nIDString + wCount, szBuffer, sizeof( szBuffer ) ) ;
      wPosition = LOWORD( SendMessage( hCtrlWnd, CB_ADDSTRING, 0,
                                       (LPARAM) (LPSTR) szBuffer ) ) ;

      // use item data to store the actual table value

      SendMessage( hCtrlWnd, CB_SETITEMDATA, (WPARAM) wPosition,
                   (LPARAM) *(npTable + wCount) ) ;

      // if this is our current setting, select it

      if (*(npTable + wCount) == dwCurrentSetting)
         SendMessage( hCtrlWnd, CB_SETCURSEL, (WPARAM) wPosition, 0L ) ;
   }

} // end of FillComboBox()

//---------------------------------------------------------------------------
//  BOOL NEAR SettingsDlgInit( HWND hDlg )
//
//  Description:
//     Puts current settings into dialog box (via CheckRadioButton() etc.)
//
//  Parameters:
//     HWND hDlg
//        handle to dialog box
//
//  Win-32 Porting Issues:
//     - Constants require DWORD arrays for baud rate table, etc.
//     - There is no "MAXCOM" function in Win-32.  Number of COM ports
//       is assumed to be 4.
//
//---------------------------------------------------------------------------

BOOL NEAR SettingsDlgInit( HWND hDlg )
{
   char       szBuffer[ MAXLEN_TEMPSTR ], szTemp[ MAXLEN_TEMPSTR ] ;
   NPTTYINFO  npTTYInfo ;
   WORD       wCount, wMaxCOM, wPosition ;

   if (NULL == (npTTYInfo = (NPTTYINFO) GET_PROP( hDlg, ATOM_TTYINFO )))
      return ( FALSE ) ;

   wMaxCOM = MAXPORTS ;

   // load the COM prefix from resources

   LoadString( GETHINST( hDlg ), IDS_COMPREFIX, szTemp, sizeof( szTemp ) ) ;

   // fill port combo box and make initial selection

   for (wCount = 0; wCount < wMaxCOM; wCount++)
   {
      wsprintf( szBuffer, "%s%d", (LPSTR) szTemp, wCount + 1 ) ;
      SendDlgItemMessage( hDlg, IDD_PORTCB, CB_ADDSTRING, 0,
                          (LPARAM) (LPSTR) szBuffer ) ;
   }

   SendDlgItemMessage( hDlg, IDD_PORTCB, CB_SETCURSEL,
                       (WPARAM) (PORT( npTTYInfo ) - 1), 0L ) ;

   // disable COM port combo box if connection has already been
   // established (e.g. OpenComm() already successful)

   EnableWindow( GetDlgItem( hDlg, IDD_PORTCB ), !CONNECTED( npTTYInfo ) ) ;

   // fill baud combo box and make initial selection

   FillComboBox( GETHINST( hDlg ), GetDlgItem( hDlg, IDD_BAUDCB ),
                 IDS_BAUD110, BaudTable,
                 sizeof( BaudTable ) / sizeof( BaudTable[ 0 ] ),
                 BAUDRATE( npTTYInfo ) ) ;

   // fill data bits combo box and make initial selection

   for (wCount = 5; wCount < 9; wCount++)
   {
      wsprintf( szBuffer, "%d", wCount ) ;
      wPosition = LOWORD( SendDlgItemMessage( hDlg, IDD_DATABITSCB,
                                              CB_ADDSTRING, 0,
                                              (LPARAM) (LPSTR) szBuffer ) ) ;

      // if current selection, tell the combo box

      if (wCount == BYTESIZE( npTTYInfo ))
         SendDlgItemMessage( hDlg, IDD_DATABITSCB, CB_SETCURSEL,
                             (WPARAM) wPosition, 0L ) ;
   }

   // fill parity combo box and make initial selection

   FillComboBox( GETHINST( hDlg ), GetDlgItem( hDlg, IDD_PARITYCB ),
                 IDS_PARITYNONE, ParityTable,
                 sizeof( ParityTable ) / sizeof( ParityTable[ 0 ] ),
                 PARITY( npTTYInfo ) ) ;

   // fill stop bits combo box and make initial selection

   FillComboBox( GETHINST( hDlg ), GetDlgItem( hDlg, IDD_STOPBITSCB ),
                 IDS_ONESTOPBIT, StopBitsTable,
                 sizeof( StopBitsTable ) / sizeof ( StopBitsTable ),
                 STOPBITS( npTTYInfo ) ) ;

   // initalize the flow control settings

   CheckDlgButton( hDlg, IDD_DTRDSR,
                   (FLOWCTRL( npTTYInfo ) & FC_DTRDSR) > 0 ) ;
   CheckDlgButton( hDlg, IDD_RTSCTS,
                   (FLOWCTRL( npTTYInfo ) & FC_RTSCTS) > 0 ) ;
   CheckDlgButton( hDlg, IDD_XONXOFF,
                   (FLOWCTRL( npTTYInfo ) & FC_XONXOFF) > 0 ) ;

   // other TTY settings

   CheckDlgButton( hDlg, IDD_AUTOWRAP, AUTOWRAP( npTTYInfo ) ) ;
   CheckDlgButton( hDlg, IDD_NEWLINE, NEWLINE( npTTYInfo ) ) ;
   CheckDlgButton( hDlg, IDD_LOCALECHO, LOCALECHO( npTTYInfo ) ) ;

   // control options

   // "Use CN_RECEIVE" is not valid under Win-32

   EnableWindow( GetDlgItem( hDlg, IDD_USECNRECEIVE ), FALSE ) ;

   CheckDlgButton( hDlg, IDD_DISPLAYERRORS, DISPLAYERRORS( npTTYInfo ) ) ;

   return ( TRUE ) ;

} // end of SettingsDlgInit()

//---------------------------------------------------------------------------
//  BOOL NEAR SelectTTYFont( HWND hDlg )
//
//  Description:
//     Selects the current font for the TTY screen.
//     Uses the Common Dialog ChooseFont() API.
//
//  Parameters:
//     HWND hDlg
//        handle to settings dialog
//
//---------------------------------------------------------------------------

BOOL NEAR SelectTTYFont( HWND hDlg )
{
   CHOOSEFONT  cfTTYFont ;
   NPTTYINFO   npTTYInfo ;

   if (NULL == (npTTYInfo = (NPTTYINFO) GET_PROP( hDlg, ATOM_TTYINFO )))
      return ( FALSE ) ;

   cfTTYFont.lStructSize    = sizeof( CHOOSEFONT ) ;
   cfTTYFont.hwndOwner      = hDlg ;
   cfTTYFont.hDC            = NULL ;
   cfTTYFont.rgbColors      = FGCOLOR( npTTYInfo ) ;
   cfTTYFont.lpLogFont      = &LFTTYFONT( npTTYInfo ) ;
   cfTTYFont.Flags          = CF_SCREENFONTS | CF_FIXEDPITCHONLY |
                              CF_EFFECTS | CF_INITTOLOGFONTSTRUCT ;
   cfTTYFont.lCustData      = 0 ;
   cfTTYFont.lpfnHook       = NULL ;
   cfTTYFont.lpTemplateName = NULL ;
   cfTTYFont.hInstance      = GETHINST( hDlg ) ;

   if (ChooseFont( &cfTTYFont ))
   {
     FGCOLOR( npTTYInfo ) = cfTTYFont.rgbColors ;
     ResetTTYScreen( GetParent( hDlg ), npTTYInfo ) ;
   }

   return ( TRUE ) ;

} // end of SelectTTYFont()

//---------------------------------------------------------------------------
//  BOOL NEAR SettingsDlgTerm( HWND hDlg )
//
//  Description:
//     Puts dialog contents into TTY info structure.
//
//  Parameters:
//     HWND hDlg
//        handle to settings dialog
//
//  Win-32 Porting Issues:
//     - Baud rate requires DWORD values.
//
//---------------------------------------------------------------------------

BOOL NEAR SettingsDlgTerm( HWND hDlg )
{
   NPTTYINFO  npTTYInfo ;
   WORD       wSelection ;

   if (NULL == (npTTYInfo = (NPTTYINFO) GET_PROP( hDlg, ATOM_TTYINFO )))
      return ( FALSE ) ;

   // get port selection

   PORT( npTTYInfo ) =
      LOBYTE( LOWORD( SendDlgItemMessage( hDlg, IDD_PORTCB,
                                          CB_GETCURSEL,
                                          0, 0L ) ) + 1 ) ;
   // get baud rate selection

   wSelection =
      LOWORD( SendDlgItemMessage( hDlg, IDD_BAUDCB, CB_GETCURSEL,
                                  0, 0L ) ) ;
   BAUDRATE( npTTYInfo ) =
      SendDlgItemMessage( hDlg, IDD_BAUDCB, CB_GETITEMDATA,
                          (WPARAM) wSelection, 0L ) ;

   // get data bits selection

   BYTESIZE( npTTYInfo ) =
      LOBYTE( LOWORD( SendDlgItemMessage( hDlg, IDD_DATABITSCB,
                                          CB_GETCURSEL,
                                          0, 0L ) ) + 5 ) ;

   // get parity selection

   wSelection =
      LOWORD( SendDlgItemMessage( hDlg, IDD_PARITYCB, CB_GETCURSEL,
                                  0, 0L ) ) ;
   PARITY( npTTYInfo ) =
      LOBYTE( LOWORD( SendDlgItemMessage( hDlg, IDD_PARITYCB,
                                          CB_GETITEMDATA,
                                          (WPARAM) wSelection,
                                          0L ) ) ) ;

   // get stop bits selection

   wSelection =
      LOWORD( SendDlgItemMessage( hDlg, IDD_STOPBITSCB, CB_GETCURSEL,
                                  0, 0L ) ) ;

STOPBITS( npTTYInfo ) =
      LOBYTE( LOWORD( SendDlgItemMessage( hDlg, IDD_STOPBITSCB,
                                          CB_GETITEMDATA,
                                          (WPARAM) wSelection, 0L ) ) ) ;

   // get flow control settings

   FLOWCTRL( npTTYInfo ) = 0 ;
   if (IsDlgButtonChecked( hDlg, IDD_DTRDSR ))
      FLOWCTRL( npTTYInfo ) |= FC_DTRDSR ;
   if (IsDlgButtonChecked( hDlg, IDD_RTSCTS ))
      FLOWCTRL( npTTYInfo ) |= FC_RTSCTS ;
   if (IsDlgButtonChecked( hDlg, IDD_XONXOFF ))
      FLOWCTRL( npTTYInfo ) |= FC_XONXOFF ;

   // get other various settings

   AUTOWRAP( npTTYInfo ) = IsDlgButtonChecked( hDlg, IDD_AUTOWRAP ) ;
   NEWLINE( npTTYInfo ) = IsDlgButtonChecked( hDlg, IDD_NEWLINE ) ;
   LOCALECHO( npTTYInfo ) = IsDlgButtonChecked( hDlg, IDD_LOCALECHO ) ;

   // control options

   USECNRECEIVE( npTTYInfo ) = IsDlgButtonChecked( hDlg, IDD_USECNRECEIVE ) ;
   DISPLAYERRORS( npTTYInfo ) = IsDlgButtonChecked( hDlg, IDD_DISPLAYERRORS ) ;

   return ( TRUE ) ;

} // end of SettingsDlgTerm()

//---------------------------------------------------------------------------
//  BOOL FAR PASCAL SettingsDlgProc( HWND hDlg, UINT uMsg,
//                                   WPARAM wParam, LPARAM lParam )
//
//  Description:
//     This handles all of the user preference settings for
//     the TTY.
//
//  Parameters:
//     same as all dialog procedures
//
//  Win-32 Porting Issues:
//     - npTTYInfo is a DWORD in Win-32.
//
//---------------------------------------------------------------------------

BOOL FAR PASCAL SettingsDlgProc( HWND hDlg, UINT uMsg,
                                 WPARAM wParam, LPARAM lParam )
{
   switch (uMsg)
   {
      case WM_INITDIALOG:
      {
         NPTTYINFO  npTTYInfo ;

         // get & save pointer to TTY info structure

         npTTYInfo = (NPTTYINFO) lParam ;

         SET_PROP( hDlg, ATOM_TTYINFO, (HANDLE) npTTYInfo ) ;

         return ( SettingsDlgInit( hDlg ) ) ;
      }

      case WM_COMMAND:
         switch ( LOWORD( wParam ))
         {
            case IDD_FONT:
               return ( SelectTTYFont( hDlg ) ) ;

            case IDD_OK:
               // Copy stuff into structure
               SettingsDlgTerm( hDlg ) ;
               EndDialog( hDlg, TRUE ) ;
               return ( TRUE ) ;

            case IDD_CANCEL:
               // Just end
               EndDialog( hDlg, TRUE ) ;
               return ( TRUE ) ;
         }
         break;

      case WM_DESTROY:
         REMOVE_PROP( hDlg, ATOM_TTYINFO ) ;
         break ;
   }
   return ( FALSE ) ;

} // end of SettingsDlgProc()

//************************************************************************
//  DWORD FAR PASCAL CommWatchProc( LPSTR lpData )
//
//  Description:
//     A secondary thread that will watch for COMM events.
//
//  Parameters:
//     LPSTR lpData
//        32-bit pointer argument
//
//  Win-32 Porting Issues:
//     - Added this thread to watch the communications device and
//       post notifications to the associated window.
//
//************************************************************************

DWORD FAR PASCAL CommWatchProc( LPSTR lpData )
{
   DWORD       dwEvtMask ;
   NPTTYINFO   npTTYInfo = (NPTTYINFO) lpData ;
   OVERLAPPED  os ;
int        nLength ;
   BYTE       abIn[ MAXBLOCK + 1] ;

   memset( &os, 0, sizeof( OVERLAPPED ) ) ;

   // create I/O event used for overlapped read

   os.hEvent = CreateEvent( NULL,    // no security
                            TRUE,    // explicit reset req
                            FALSE,   // initial event reset
                            NULL ) ; // no name
   if (os.hEvent == NULL)
   {
      MessageBox( NULL, "Failed to create event for thread!", "TTY Error!",
                  MB_ICONEXCLAMATION | MB_OK ) ;
      return ( FALSE ) ;
   }

   if (!SetCommMask( COMDEV( npTTYInfo ), EV_RXCHAR ))
      return ( FALSE ) ;

   while ( CONNECTED( npTTYInfo ) )
   {
      dwEvtMask = 0 ;

      WaitCommEvent( COMDEV( npTTYInfo ), &dwEvtMask, NULL );

      if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR)
      {
         do
         {
            if (nLength = ReadCommBlock( hTTYWnd, (LPSTR) abIn, MAXBLOCK ))
            {
               WriteTTYBlock( hTTYWnd, (LPSTR) abIn, nLength ) ;

               // force a paint

               UpdateWindow( hTTYWnd ) ;
            }
         }
         while ( nLength > 0 ) ;
      }
   }

   // get rid of event handle

   CloseHandle( os.hEvent ) ;

   // clear information in structure (kind of a "we're done flag")

   THREADID( npTTYInfo ) = 0 ;
   HTHREAD( npTTYInfo ) = NULL ;

   return( TRUE ) ;

} // end of CommWatchProc()

//---------------------------------------------------------------------------
//  End of File: tty.c
//---------------------------------------------------------------------------


TTY.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corp.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define USECOMM      // yes, we need the COMM API

//#undef NO_STRICT    // be bold!

//#define HINSTANCE HANDLE

#include <windows.h>
#include <commdlg.h>
#include <string.h>
#include <io.h>
#include <memory.h>

#include "version.h"
#include "resource.h"

// constant definitions

#define GWL_NPTTYINFO        0
#define TTYEXTRABYTES        sizeof( LONG )

#define ABOUTDLG_USEBITMAP  1

#define ATOM_TTYINFO       0x100

// hard coded maximum number of ports for device under Win32

#define MAXPORTS        4

// terminal size

#define MAXROWS         25
#define MAXCOLS         80

#define MAXBLOCK        80

#define MAXLEN_TEMPSTR  81

#define RXQUEUE         4096
#define TXQUEUE         4096

// cursor states

#define CS_HIDE         0x00
#define CS_SHOW         0x01

// Flow control flags

#define FC_DTRDSR       0x01
#define FC_RTSCTS       0x02
#define FC_XONXOFF      0x04

// ascii definitions

#define ASCII_BEL       0x07
#define ASCII_BS        0x08
#define ASCII_LF        0x0A
#define ASCII_CR        0x0D
#define ASCII_XON       0x11
#define ASCII_XOFF      0x13

// data structures

typedef struct tagTTYINFO
{
   HANDLE  idComDev ;
   BYTE    bPort, abScreen[ MAXROWS * MAXCOLS ] ;
   BOOL    fConnected, fXonXoff, fLocalEcho, fNewLine, fAutoWrap,
           fUseCNReceive, fDisplayErrors;
   BYTE    bByteSize, bFlowCtrl, bParity, bStopBits ;
   DWORD   dwBaudRate ;
   WORD    wCursorState ;
   HFONT   hTTYFont ;
   LOGFONT lfTTYFont ;
   DWORD   rgbFGColor ;
   int     xSize, ySize, xScroll, yScroll, xOffset, yOffset,
           nColumn, nRow, xChar, yChar ;
   HANDLE      hPostEvent, hWatchThread, hWatchEvent ;
   HWND        hTermWnd ;
   DWORD       dwThreadID ;
   OVERLAPPED  osWrite, osRead ;

} TTYINFO, NEAR *NPTTYINFO ;

// macros ( for easier readability )

#define GETHINST( x )  ((HINSTANCE) GetWindowLong( x, GWL_HINSTANCE ))
#define GETNPTTYINFO( x ) ((NPTTYINFO) GetWindowLong( x, GWL_NPTTYINFO ))
#define SETNPTTYINFO( x, y ) SetWindowLong( x, GWL_NPTTYINFO, (LONG) y )

#define COMDEV( x ) (x -> idComDev)
#define PORT( x )   (x -> bPort)
#define SCREEN( x ) (x -> abScreen)
#define CONNECTED( x ) (x -> fConnected)
#define XONXOFF( x ) (x -> fXonXoff)
#define LOCALECHO( x ) (x -> fLocalEcho)
#define NEWLINE( x ) (x -> fNewLine)
#define AUTOWRAP( x ) (x -> fAutoWrap)
#define BYTESIZE( x ) (x -> bByteSize)
#define FLOWCTRL( x ) (x -> bFlowCtrl)
#define PARITY( x ) (x -> bParity)
#define STOPBITS( x ) (x -> bStopBits)
#define BAUDRATE( x ) (x -> dwBaudRate)
#define CURSORSTATE( x ) (x -> wCursorState)
#define HTTYFONT( x ) (x -> hTTYFont)
#define LFTTYFONT( x ) (x -> lfTTYFont)
#define FGCOLOR( x ) (x -> rgbFGColor)
#define XSIZE( x ) (x -> xSize)
#define YSIZE( x ) (x -> ySize)
#define XSCROLL( x ) (x -> xScroll)
#define YSCROLL( x ) (x -> yScroll)
#define XOFFSET( x ) (x -> xOffset)
#define YOFFSET( x ) (x -> yOffset)
#define COLUMN( x ) (x -> nColumn)
#define ROW( x ) (x -> nRow)
#define XCHAR( x ) (x -> xChar)
#define YCHAR( x ) (x -> yChar )
#define USECNRECEIVE( x ) (x -> fUseCNReceive)
#define DISPLAYERRORS( x ) (x -> fDisplayErrors)

#define POSTEVENT( x ) (x -> hPostEvent)
#define TERMWND( x ) (x -> hTermWnd)
#define HTHREAD( x ) (x -> hWatchThread)
#define THREADID( x ) (x -> dwThreadID)
#define WRITE_OS( x ) (x -> osWrite)
#define READ_OS( x ) (x -> osRead)

#define SET_PROP( x, y, z )  SetProp( x, MAKEINTATOM( y ), z )
#define GET_PROP( x, y )     GetProp( x, MAKEINTATOM( y ) )
#define REMOVE_PROP( x, y )  RemoveProp( x, MAKEINTATOM( y ) )

// global stuff

HWND     hTTYWnd ;
char     gszTTYClass[] = "TTYWndClass" ;
char     gszAppName[] = "TTY" ;
HANDLE   ghAccel ;

DWORD    BaudTable[] =
         {
            CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400,
            CBR_4800, CBR_9600, CBR_14400, CBR_19200, CBR_38400,
            CBR_56000, CBR_128000, CBR_256000
         } ;

DWORD    ParityTable[] =
         {
            NOPARITY, EVENPARITY, ODDPARITY, MARKPARITY, SPACEPARITY
         } ;

DWORD    StopBitsTable[] =
         {
            ONESTOPBIT, ONE5STOPBITS, TWOSTOPBITS
         } ;

// CRT mappings to NT API

#define _fmemset   memset
#define _fmemmove  memmove

// function prototypes (private)

BOOL NEAR InitApplication( HANDLE ) ;
HWND NEAR InitInstance( HANDLE, int ) ;
LRESULT NEAR CreateTTYInfo( HWND ) ;
BOOL NEAR DestroyTTYInfo( HWND ) ;
BOOL NEAR ResetTTYScreen( HWND, NPTTYINFO ) ;
BOOL NEAR KillTTYFocus( HWND ) ;
BOOL NEAR PaintTTY( HWND ) ;
BOOL NEAR SetTTYFocus( HWND ) ;
BOOL NEAR ScrollTTYHorz( HWND, WORD, WORD ) ;
BOOL NEAR ScrollTTYVert( HWND, WORD, WORD ) ;
BOOL NEAR SizeTTY( HWND, WORD, WORD ) ;
BOOL NEAR ProcessTTYCharacter( HWND, BYTE ) ;
BOOL NEAR WriteTTYBlock( HWND, LPSTR, int ) ;
int NEAR ReadCommBlock( HWND, LPSTR, int ) ;
BOOL NEAR WriteCommBlock( HWND , LPSTR, DWORD);
BOOL NEAR MoveTTYCursor( HWND ) ;
BOOL NEAR OpenConnection( HWND ) ;
BOOL NEAR SetupConnection( HWND ) ;
BOOL NEAR CloseConnection( HWND ) ;
VOID NEAR GoModalDialogBoxParam( HINSTANCE, LPCSTR, HWND, DLGPROC, LPARAM ) ;
VOID NEAR FillComboBox( HINSTANCE, HWND, int, DWORD NEAR *, WORD, DWORD ) ;
BOOL NEAR SelectTTYFont( HWND ) ;
BOOL NEAR SettingsDlgInit( HWND ) ;
BOOL NEAR SettingsDlgTerm( HWND ) ;

// function prototypes (public)

LRESULT FAR PASCAL TTYWndProc( HWND, UINT, WPARAM, LPARAM ) ;
BOOL FAR PASCAL AboutDlgProc( HWND, UINT, WPARAM, LPARAM ) ;
BOOL FAR PASCAL SettingsDlgProc( HWND, UINT, WPARAM, LPARAM ) ;

DWORD FAR PASCAL CommWatchProc( LPSTR ) ;

//---------------------------------------------------------------------------
//  End of File: tty.h
//---------------------------------------------------------------------------


VERSION.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define VER_MAJOR 3
#define VER_MINOR 10
#define VER_BUILD 0


README.TXT

Fast Copy Routines

The UNBUFCPY sample consists of two parts: UNBUFCP1 and UNBUFCP2.

The UNBUFCP1 sample shows a fast copy routine that uses I/O completion
ports. It is intended to demonstrate using a single thread to complete I/O
requests from multiple file handles.

Only one thread and one I/O completion port is used. The file handles for
the source and destination files are both associated with the same port. The
thread starts by posting a number of overlapped reads from the source file.
It then waits on the I/O completion port. Whenever a read completes, it
immediately turns it into a write to the destination file. Whenever a write
completes, it immediately posts the next read from the source file.

The UNBUFCP2 sample shows a fast copy routine that uses I/O completion
ports. It is intended to demonstrate how to complete I/O in a different
thread than the thread that started the I/O.

Two threads are used. The first thread posts overlapped reads from the
source file. These reads complete to an I/O completion port the second
thread is waiting on. The second thread sees the I/O completion and posts
an overlapped write to the destination file. The write completes to another I/O completion
port that the first thread is waiting on. The first thread sees the I/O
completion and posts another overlapped read.


UNBUFCP1.C

/*++

Copyright 1994 - 1998 Microsoft Corporation

Module Name:

    unbufcp1.c

Abstract:

    This single-threaded version shows how to multiplex I/O to a number of files with
    a single thread. This is the most efficient mechanism if you do not need the
    asynchronous completion model that the dual-threaded version offers.

    Only one thread and one I/O completion port is used. The file handles for the
    source and destination file are both associated with the same port.  The
    thread starts off by posting a number of overlapped
    reads from the source file.  It then waits on the I/O completion port.
    Whenever a read completes, it immediately turns it around into a write to
    the destination file. Whenever a write completes, it immediately posts the
    next read from the source file.

    Thread 1
       |
       |
    kick off a few
    overlapped reads
       |
       |
  ->GetQueuedCompletionStatus(WritePort) <-----------
  |    |                                            |
  |    |-------------------------------             |
  |    |                              |             |
  | write has completed,      read has completed,   |
  | kick off another          kick off the write.   |
  | read                              |             |
  |    |                              |             |
  |____|                              |_____________|

Author:

    John Vert (jvert) 21-Dec-1994

Revision History:

    24-Apr-1995       Brian Sherrell (briansh) - Added platform detection &
                      I/O Completion Port creation with INVALID_HANDLE_VALUE.

    22-March-1996     Brian Sherrell (briansh) - Pending I/O bug fix.

 3-Apr-1996       Brian Sherrell (briansh) - Correct versioning logic.

--*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

//
// File handles for the copy operation. All read operations are
// from SourceFile. All write operations are to DestFile.
//
HANDLE SourceFile;
HANDLE DestFile;

//
//version information
//
OSVERSIONINFO     ver;

//
// I/O completion port. All read and writes to the files complete
// to this port.
//
HANDLE IoPort;

//
// Key values used to determine whether a read or a write
// has completed.
//
#define ReadKey 0
#define WriteKey 1

//
// Structure used to track each outstanding I/O. The maximum
// number of I/Os that will be outstanding at any time is
// controllable by the MAX_CONCURRENT_IO definition.
//

#define MAX_CONCURRENT_IO 20

typedef struct _COPY_CHUNK {
    OVERLAPPED Overlapped;
    LPVOID Buffer;
} COPY_CHUNK, *PCOPY_CHUNK;

COPY_CHUNK CopyChunk[MAX_CONCURRENT_IO];

//
// Define the size of the buffers used to do the I/O.
// 64K is a nice number.
//
#define BUFFER_SIZE (64*1024)

//
// The system's page size will always be a multiple of the
// sector size. Do all I/Os in page-size chunks.
//
DWORD PageSize;

//
// Local function prototype
//
VOID
CopyLoop(
    ULARGE_INTEGER FileSize
    );

int
_CRTAPI1
main(
    int argc,
    char *argv[],
    char *envp
    )
{
    ULARGE_INTEGER FileSize;
    ULARGE_INTEGER InitialSize;
    BOOL Success;
    DWORD Status;
    DWORD StartTime, EndTime;
    SYSTEM_INFO SystemInfo;
    HANDLE BufferedHandle;

    if (argc != 3) {
        fprintf(stderr, "Usage: %s SourceFile DestinationFile\n", argv[0]);
        exit(1);
    }

    //
    //confirm we are running on Windows NT 3.5 or greater, if not, display notice and
    //terminate.  Completion ports are only supported on Win32 & Win32s.  Creating a
    //Completion port with no handle specified is only supported on NT 3.51, so we need
    //to know what we're running on.  Note, Win32s does not support console apps, thats
    //why we exit here if we are not on Windows NT.
    //
    //
    //ver.dwOSVersionInfoSize needs to be set before calling GetVersionInfoEx()
    //
    ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

    //
    //Failure here could mean several things 1. On an NT system,
    //it indicates NT version 3.1 because GetVersionEx() is only
    //implemented on NT 3.5.  2. On Windows 3.1 system, it means
    //either Win32s version 1.1 or 1.0 is installed.
    //
    Success = GetVersionEx((LPOSVERSIONINFO) &ver);

    if ( (!Success) ||                                   //GetVersionEx() failed - see above.
         (ver.dwPlatformId != VER_PLATFORM_WIN32_NT) )   //GetVersionEx() succeeded but we are not on NT.
      {
       MessageBox(NULL,
                  "This sample application can only be run on Windows NT. 3.5 or greater\n"
                  "This application will now terminate.",
                  "UnBufCp1",
                  MB_OK           |
                  MB_ICONSTOP     |
                  MB_SETFOREGROUND );
       exit( 1 );
      }

    //
    // Get the system's page size.
    //
    GetSystemInfo(&SystemInfo);
    PageSize = SystemInfo.dwPageSize;

    //
    // Open the source file and create the destination file.
    // Use FILE_FLAG_NO_BUFFERING to avoid polluting the
    // system cache with two copies of the same data.
    //

    SourceFile = CreateFile(argv[1],
                            GENERIC_READ | GENERIC_WRITE,
                            FILE_SHARE_READ,
                            NULL,
                            OPEN_EXISTING,
                            FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
                            NULL);
    if (SourceFile == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "failed to open %s, error %d\n", argv[1], GetLastError());
        exit(1);
    }
    FileSize.LowPart = GetFileSize(SourceFile, &FileSize.HighPart);
    if ((FileSize.LowPart == 0xffffffff) && (GetLastError() != NO_ERROR)) {
        fprintf(stderr, "GetFileSize failed, error %d\n", GetLastError());
        exit(1);
    }

    DestFile = CreateFile(argv[2],
                          GENERIC_READ | GENERIC_WRITE,
                          FILE_SHARE_READ | FILE_SHARE_WRITE,
                          NULL,
                          CREATE_ALWAYS,
                          FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
                          SourceFile);
    if (DestFile == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "failed to open %s, error %d\n", argv[2], GetLastError());
        exit(1);
    }

    //
    // Extend the destination file so that the filesystem does not
    // turn our asynchronous writes into synchronous ones.
    //
    InitialSize.QuadPart = (FileSize.QuadPart + PageSize - 1) & ~(PageSize-1);
    Status = SetFilePointer(DestFile,
                            InitialSize.LowPart,
                            (PLONG)&InitialSize.HighPart,
                            FILE_BEGIN);
    if ((Status == 0xffffffff) && (GetLastError() != NO_ERROR)) {
        fprintf(stderr, "initial SetFilePointer failed, error %d\n", GetLastError());
        exit(1);
    }
    Success = SetEndOfFile(DestFile);
    if (!Success) {
        fprintf(stderr, "SetEndOfFile failed, error %d\n", GetLastError());
        exit(1);
    }

    //
    //In NT 3.51 it is not necessary to specify the FileHandle parameter
    //of CreateIoCompletionPort()--It is legal to specify the FileHandle
    //as INVALID_HANDLE_VALUE.  However, for NT 3.5 an overlapped file
    //handle is needed.
    //
    //We know already that we are running on NT, or else we wouldn't have
    //gotten this far, so lets see what version we are running on.
    //
    if (ver.dwMajorVersion == 3 && ver.dwMinorVersion == 50) //we're running on NT 3.5 - Completion Ports exists.
      IoPort = CreateIoCompletionPort(SourceFile,            //file handle to associate with I/O completion port.
                                      NULL,                  //optional handle to existing I/O completion port.
                                      ReadKey,               //completion key.
                                      1);                    //# of threads allowed to execute concurrently.
    else
        {
          //                                                 //we are running on NT 3.51 or greater
          //Create the I/O Completion Port
          //
          IoPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,//file handle to associate with I/O completion port
                                          NULL,                //optional handle to existing I/O completion port
                                          ReadKey,             //completion key
                                          1);                  //# of threads allowed to execute concurrently

          if (IoPort == NULL) {
            fprintf(stderr, "failed to create ReadPort, error %d\n", GetLastError());
            exit(1);
          }

         //
         //If we need to, aka we're running on NT 3.51, let's associate a file handle with the
         //completion port.
         //

         IoPort = CreateIoCompletionPort(SourceFile,
                                         IoPort,
                                         ReadKey,
                                         1);

         if (IoPort == NULL)
           {
             fprintf(stderr,
                     "failed to create IoPort, error %d\n",
                     GetLastError());

             exit(1);

           }
        }

    //
    // Associate the destination file handle with the
    // I/O completion port.
    //

    IoPort = CreateIoCompletionPort(DestFile,
                                    IoPort,
                                    WriteKey,
                                    1);
    if (IoPort == NULL) {
        fprintf(stderr, "failed to create WritePort, error %d\n", GetLastError());
        exit(1);
    }

    StartTime = GetTickCount();

    //
    // Do the copy
    //
    CopyLoop(FileSize);

    EndTime = GetTickCount();

    CloseHandle(SourceFile);
    CloseHandle(DestFile);

    //
    // We need another handle to the destination file that is
    // opened without FILE_FLAG_NO_BUFFERING. This allows us to set
    // the end-of-file marker to a position that is not sector-aligned.
    //
    BufferedHandle = CreateFile(argv[2],
                                GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                0,
                                NULL);
    if (BufferedHandle == INVALID_HANDLE_VALUE) {
        fprintf(stderr,
                "failed to open buffered handle to %s, error %d\n",
                argv[2],
                GetLastError());
        exit(1);
    }

    //
    // Set the destination's file size to the size of the
    // source file, in case the size of the source file was
    // not a multiple of the page size.
    //
    Status = SetFilePointer(BufferedHandle,
                            FileSize.LowPart,
                            (PLONG)&FileSize.HighPart,
                            FILE_BEGIN);
    if ((Status == 0xffffffff) && (GetLastError() != NO_ERROR)) {
        fprintf(stderr, "final SetFilePointer failed, error %d\n", GetLastError());
        exit(1);
    }
    Success = SetEndOfFile(BufferedHandle);
    if (!Success) {
        fprintf(stderr, "SetEndOfFile failed, error %d\n", GetLastError());
        exit(1);
    }
    CloseHandle(BufferedHandle);

    printf("%d bytes copied in %.3f seconds\n",
           FileSize.LowPart,
           (float)(EndTime-StartTime)/1000.0);
    printf("%.2f MB/sec\n",
           ((LONGLONG)FileSize.QuadPart/(1024.0*1024.0)) / (((float)(EndTime-StartTime)) / 1000.0));

    return(0);

}

VOID
CopyLoop(
    ULARGE_INTEGER FileSize
    )
{
    ULARGE_INTEGER ReadPointer;
    BOOL Success;
    DWORD NumberBytes;
    LPOVERLAPPED CompletedOverlapped;
    DWORD Key;
    PCOPY_CHUNK Chunk;
    int PendingIO = 0;
    int i;

    //
    // Start reading the file. Kick off MAX_CONCURRENT_IO reads, then just
    // loop waiting for I/O to complete.
    //
    ReadPointer.QuadPart = 0;

    for (i=0; i < MAX_CONCURRENT_IO; i++) {
        if (ReadPointer.QuadPart >= FileSize.QuadPart) {
            break;
        }
        //
        // Use VirtualAlloc so we get a page-aligned buffer suitable
        // for unbuffered I/O.
        //
        CopyChunk[i].Buffer = VirtualAlloc(NULL,
                                           BUFFER_SIZE,
                                           MEM_COMMIT,
                                           PAGE_READWRITE);
        if (CopyChunk[i].Buffer == NULL) {
            fprintf(stderr, "VirtualAlloc %d failed, error %d\n",i, GetLastError());
            exit(1);
        }
        CopyChunk[i].Overlapped.Offset = ReadPointer.LowPart;
        CopyChunk[i].Overlapped.OffsetHigh = ReadPointer.HighPart;
        CopyChunk[i].Overlapped.hEvent = NULL;     // not needed

        Success = ReadFile(SourceFile,
                           CopyChunk[i].Buffer,
                           BUFFER_SIZE,
                           &NumberBytes,
                           &CopyChunk[i].Overlapped);

        if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
            fprintf(stderr,
                    "ReadFile at %lx failed, error %d\n",
                    ReadPointer.LowPart,
                    GetLastError());
        } else {
            ReadPointer.QuadPart += BUFFER_SIZE;
            ++PendingIO;
        }
    }

    //
    // We have started the initial async. reads, enter the main loop.
    // This simply waits until an I/O completes, then issues the next
    // I/O.  When a write completes, the next read is issued. When a
    // read completes, the corresponding write is issued.
    //
    while (PendingIO) {
        Success = GetQueuedCompletionStatus(IoPort,
                                            &NumberBytes,
                                            &Key,
                                            &CompletedOverlapped,
                                            (DWORD)-1);
        if (!Success && (CompletedOverlapped == NULL)) {
            //
            // The call has failed.
            //
            fprintf(stderr,
                    "GetQueuedCompletionStatus on the IoPort failed, error %d\n",
                    GetLastError());
            exit(1);
        }
        if (!Success) {
            //
            // The call has succeeded, but the initial I/O operation
            // has failed.
            //
            fprintf(stderr,
                    "GetQueuedCompletionStatus on the IoPort removed a failed\n"
                    "I/O packet, error %d\n",GetLastError());
            //
            // This is probably bad, but what the heck, it's only a demo program,
            // so proceed blindly ahead.
            //
        }

        Chunk = (PCOPY_CHUNK)CompletedOverlapped;

        if (Key == ReadKey) {

            //
            // A read has completed, issue the corresponding write.
            //

            //
            // Round the number of bytes to write up to a sector boundary.
            //
            NumberBytes = (NumberBytes + PageSize - 1) & ~(PageSize-1);

            Success = WriteFile(DestFile,
                                Chunk->Buffer,
                                NumberBytes,
                                &NumberBytes,
                                &Chunk->Overlapped);

            if (!Success && (GetLastError() != ERROR_IO_PENDING))
                fprintf(stderr,
                        "WriteFile at %lx failed, error %d\n",
                        Chunk->Overlapped.Offset,
                        GetLastError());

            //
            // decrement pending I/O count
            //
            --PendingIO;

        } else if (Key == WriteKey) {

            //
            // A write has completed, issue the next read.
            //

            if (ReadPointer.QuadPart < FileSize.QuadPart) {
                Chunk->Overlapped.Offset = ReadPointer.LowPart;
                Chunk->Overlapped.OffsetHigh = ReadPointer.HighPart;
                ReadPointer.QuadPart += BUFFER_SIZE;
                Success = ReadFile(SourceFile,
                                   Chunk->Buffer,
                                   NumberBytes,
                                   &NumberBytes,
                                   &Chunk->Overlapped);
                if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
                    fprintf(stderr,
                            "ReadFile at %lx failed, error %d\n",
                            Chunk->Overlapped.Offset,
                            GetLastError());
                }
            } else {
                //
                // There are no more reads left to issue, just
                // wait for the pending writes to drain.
                //
                --PendingIO;
            }
        }
    }
}


UNBUFCP2.C

/*++

Copyright 1994 - 1998 Microsoft Corporation

Module Name:

    unbufcp2.c

Abstract:

    Intended to demonstrate how to complete I/O in a different thread
    asynchronously than the I/O was started from.  This is useful for
    people who want a more powerful mechanism of asynchronous completion
    callbacks than Win32 provides (i.e. VMS developers who are used to ASTs).

    Two threads are used. The first thread posts overlapped reads from the
    source file. These reads complete to an I/O completion port the second
    thread is waiting on. The second thread sees the I/O completion and
    posts an overlapped write to the destination file. The write completes
    to another I/O completion port that the first thread is waiting on.
    The first thread sees the I/O completion and posts another overlapped
    read.

    Thread 1                                        Thread 2
       |                                               |
       |                                               |
    kick off a few                           -->GetQueuedCompletionStatus(ReadPort)
    overlapped reads                         |         |
       |                                     |         |
       |                                     |  read has completed,
  ->GetQueuedCompletionStatus(WritePort)     |  kick off the corresponding
  |    |                                     |  write.
  | write has completed,                     |         |
  | kick off another                         |_________|
  | read
  |    |
  |____|

Author:

    John Vert (jvert) 21-Dec-1994

Revision History:

    24-Apr-1995       Brian Sherrell (briansh) - Added platform detection &
                      I/O Completion Port creation with INVALID_HANDLE_VALUE.

    22-March-1996     Brian Sherrell (briansh) - Fixed error on files greater
                      than 1 Meg.

     3-Apr-1996       Brian Sherrell (briansh) - Correct versioning logic.

--*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

//
// File handles for the copy operation. All read operations are
// from SourceFile. All write operations are to DestFile.
//
HANDLE SourceFile;
HANDLE DestFile;

//
// I/O completion ports. All reads from the source file complete
// to ReadPort. All writes to the destination file complete to
// WritePort.
//
HANDLE ReadPort;
HANDLE WritePort;

//
//version information
//
OSVERSIONINFO     ver;

//
// Structure used to track each outstanding I/O. The maximum
// number of I/Os that will be outstanding at any time is
// controllable by the MAX_CONCURRENT_IO definition.
//

#define MAX_CONCURRENT_IO 20

typedef struct _COPY_CHUNK {
    OVERLAPPED Overlapped;
    LPVOID Buffer;
} COPY_CHUNK, *PCOPY_CHUNK;

COPY_CHUNK CopyChunk[MAX_CONCURRENT_IO];

//
// Define the size of the buffers used to do the I/O.
// 64K is a nice number.
//
#define BUFFER_SIZE (64*1024)

//
// The system's page size will always be a multiple of the
// sector size. Do all I/Os in page-size chunks.
//
DWORD PageSize;

//
// Local function prototypes
//
VOID
WINAPI
WriteLoop(
    ULARGE_INTEGER FileSize
    );

VOID
ReadLoop(
    ULARGE_INTEGER FileSize
    );

int
_CRTAPI1
main(
    int argc,
    char *argv[],
    char *envp
    )
{
    HANDLE WritingThread;
    DWORD ThreadId;
    ULARGE_INTEGER FileSize;
    ULARGE_INTEGER InitialFileSize;
    BOOL Success;
    DWORD Status;
    DWORD StartTime, EndTime;
    SYSTEM_INFO SystemInfo;
    HANDLE BufferedHandle;

    if (argc != 3) {
        fprintf(stderr, "Usage: %s SourceFile DestinationFile\n", argv[0]);
        exit(1);
    }

    //
    //confirm we are running on Windows NT 3.5 or greater, if not, display notice and
    //terminate.  Completion ports are only supported on Win32 & Win32s.  Creating a
    //Completion port with no handle specified is only supported on NT 3.51, so we need
    //to know what we're running on.  Note, Win32s does not support console apps, thats
    //why we exit here if we are not on Windows NT.
    //
    //
    //ver.dwOSVersionInfoSize needs to be set before calling GetVersionInfoEx()
    //
    ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

    //
    //Failure here could mean several things 1. On an NT system,
    //it indicates NT version 3.1 because GetVersionEx() is only
    //implemented on NT 3.5.  2. On Windows 3.1 system, it means
    //either Win32s version 1.1 or 1.0 is installed.
    //
    Success = GetVersionEx((LPOSVERSIONINFO) &ver);

    if ( (!Success) ||                                   //GetVersionEx() failed - see above.
         (ver.dwPlatformId != VER_PLATFORM_WIN32_NT) )   //GetVersionEx() succeeded but we are not on NT.
      {
       MessageBox(NULL,
                  "This sample application can only be run on Windows NT. 3.5 or greater\n"
                  "This application will now terminate.",
                  "UnBufCp2",
                  MB_OK           |
                  MB_ICONSTOP     |
                  MB_SETFOREGROUND );
       exit( 1 );
      }

    //
    // Get the system's page size.
    //
    GetSystemInfo(&SystemInfo);
    PageSize = SystemInfo.dwPageSize;

    //
    // Open the source file and create the destination file.
    // Use FILE_FLAG_NO_BUFFERING to avoid polluting the
    // system cache with two copies of the same data.
    //

    SourceFile = CreateFile(argv[1],
                            GENERIC_READ | GENERIC_WRITE,
                            FILE_SHARE_READ,
                            NULL,
                            OPEN_EXISTING,
                            FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
                            NULL);
    if (SourceFile == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "failed to open %s, error %d\n", argv[1], GetLastError());
        exit(1);
    }
    FileSize.LowPart = GetFileSize(SourceFile, &FileSize.HighPart);
    if ((FileSize.LowPart == 0xffffffff) && (GetLastError() != NO_ERROR)) {
        fprintf(stderr, "GetFileSize failed, error %d\n", GetLastError());
        exit(1);
    }

    DestFile = CreateFile(argv[2],
                          GENERIC_READ | GENERIC_WRITE,
                          FILE_SHARE_READ | FILE_SHARE_WRITE,
                          NULL,
                          CREATE_ALWAYS,
                          FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
                          SourceFile);
    if (DestFile == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "failed to open %s, error %d\n", argv[2], GetLastError());
        exit(1);
    }

    //
    // Extend the destination file so that the filesystem does not
    // turn our asynchronous writes into synchronous ones.
    //
    InitialFileSize.QuadPart = (FileSize.QuadPart + PageSize - 1) & ~(PageSize - 1);
    Status = SetFilePointer(DestFile,
                            InitialFileSize.LowPart,
                            (PLONG)&InitialFileSize.HighPart,
                            FILE_BEGIN);
    if ((Status == 0xffffffff) && (GetLastError() != NO_ERROR)) {
        fprintf(stderr, "SetFilePointer failed, error %d\n", GetLastError());
        exit(1);
    }
    Success = SetEndOfFile(DestFile);
    if (!Success) {
        fprintf(stderr, "SetEndOfFile failed, error %d\n", GetLastError());
        exit(1);
    }

    //
    //In NT 3.51 it is not necessary to specify the FileHandle parameter
    //of CreateIoCompletionPort()--It is legal to specify the FileHandle
    //as INVALID_HANDLE_VALUE.  However, for NT 3.5 an overlapped file
    //handle is needed.
    //
    //We know already that we are running on NT, or else we wouldn't have
    //gotten this far, so lets see what version we are running on.
    //
    if (ver.dwMajorVersion == 3 && ver.dwMinorVersion == 50) //we're running on NT 3.5 - Completion Ports exists
        {
          ReadPort = CreateIoCompletionPort(SourceFile,        //file handle to associate with I/O completion port
                                            NULL,              //optional handle to existing I/O completion port
                                            (DWORD)SourceFile, //completion key
                                            1);                //# of threads allowed to execute concurrently

          if (ReadPort == NULL) {
            fprintf(stderr, "failed to create ReadPort, error %d\n", GetLastError());
            exit(1);
          }
        }

    else   //we are running on NT 3.51 or greater
      //
      //Create the I/O Completion Port
      //
        {
          ReadPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, //file handle to associate with I/O completion port
                                            NULL,                 //optional handle to existing I/O completion port
                                            (DWORD)SourceFile,    //completion key
                                            1);                   //# of threads allowed to execute concurrently

         //
         //If we need to, aka we're running on NT 3.51, let's associate a file handle with the
         //completion port.
         //
         ReadPort = CreateIoCompletionPort(SourceFile,
                                           ReadPort,
                                           (DWORD)SourceFile,  //should be the previously specified key.
                                           1);

         if (ReadPort == NULL)
          {
            fprintf(stderr,
                    "failed to create ReadPort, error %d\n",
                    GetLastError());

            exit(1);

          }
        }

    WritePort = CreateIoCompletionPort(DestFile,
                                       NULL,
                                       (DWORD)DestFile,
                                       1);
    if (WritePort == NULL) {
        fprintf(stderr, "failed to create WritePort, error %d\n", GetLastError());
        exit(1);
    }

    //
    // Start the writing thread
    //
    WritingThread = CreateThread(NULL,
                                 0,
                                 (LPTHREAD_START_ROUTINE)WriteLoop,
                                 &FileSize,
                                 0,
                                 &ThreadId);
    if (WritingThread == NULL) {
        fprintf(stderr, "failed to create write thread, error %d\n", GetLastError());
        exit(1);
    }
    CloseHandle(WritingThread);

    StartTime = GetTickCount();

    //
    // Start the reads
    //
    ReadLoop(FileSize);

    EndTime = GetTickCount();

    //
    // We need another handle to the destination file that is
    // opened without FILE_FLAG_NO_BUFFERING. This allows us to set
    // the end-of-file marker to a position that is not sector-aligned.
    //
    BufferedHandle = CreateFile(argv[2],
                                GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                0,
                                NULL);
    if (BufferedHandle == INVALID_HANDLE_VALUE) {
        fprintf(stderr,
                "failed to open buffered handle to %s, error %d\n",
                argv[2],
                GetLastError());
        exit(1);
    }

    //
    // Set the destination's file size to the size of the
    // source file, in case the size of the source file was
    // not a multiple of the page size.
    //
    Status = SetFilePointer(BufferedHandle,
                            FileSize.LowPart,
                            (PLONG)&FileSize.HighPart,
                            FILE_BEGIN);
    if ((Status == 0xffffffff) && (GetLastError() != NO_ERROR)) {
        fprintf(stderr, "final SetFilePointer failed, error %d\n", GetLastError());
        exit(1);
    }
    Success = SetEndOfFile(BufferedHandle);
    if (!Success) {
        fprintf(stderr, "SetEndOfFile failed, error %d\n", GetLastError());
        exit(1);
    }
    CloseHandle(BufferedHandle);

    printf("\n\n%d bytes copied in %.3f seconds\n",
           FileSize.LowPart,
           (float)(EndTime-StartTime)/1000.0);
    printf("%.2f MB/sec\n",
           ((LONGLONG)FileSize.QuadPart/(1024.0*1024.0)) / (((float)(EndTime-StartTime)) / 1000.0));

    return(0);

}

VOID
ReadLoop(
    ULARGE_INTEGER FileSize
    )
{
    ULARGE_INTEGER ReadPointer;
    BOOL Success;
    DWORD NumberBytes;
    LPOVERLAPPED CompletedOverlapped;
    DWORD Key;
    PCOPY_CHUNK Chunk;
    int PendingIO = 0;
    int i;

    //
    // Start reading the file. Kick off MAX_CONCURRENT_IO reads, then just
    // loop waiting for writes to complete.
    //
    ReadPointer.QuadPart = 0;

    for (i=0; i < MAX_CONCURRENT_IO; i++) {
        if (ReadPointer.QuadPart >= FileSize.QuadPart) {
            break;
        }
        //
        // Use VirtualAlloc so we get a page-aligned buffer suitable
        // for unbuffered I/O.
        //
        CopyChunk[i].Buffer = VirtualAlloc(NULL,
                                           BUFFER_SIZE,
                                           MEM_COMMIT,
                                           PAGE_READWRITE);
        if (CopyChunk[i].Buffer == NULL) {
            fprintf(stderr, "VirtualAlloc %d failed, error %d\n",i, GetLastError());
            exit(1);
        }
        CopyChunk[i].Overlapped.Offset = ReadPointer.LowPart;
        CopyChunk[i].Overlapped.OffsetHigh = ReadPointer.HighPart;
        CopyChunk[i].Overlapped.hEvent = NULL;     // not needed

        Success = ReadFile(SourceFile,
                           CopyChunk[i].Buffer,
                           BUFFER_SIZE,
                           &NumberBytes,
                           &CopyChunk[i].Overlapped);

        if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
            fprintf(stderr,
                    "ReadFile at %lx failed, error %d\n",
                    ReadPointer.LowPart,
                    GetLastError());
        } else {
            ReadPointer.QuadPart += BUFFER_SIZE;
            ++PendingIO;

        }
    }

    //
    // We have started the initial async. reads, enter the main loop.
    // This simply waits until a write completes, then issues the next
    // read.
    //
    while (PendingIO) {
        Success = GetQueuedCompletionStatus(WritePort,
                                            &NumberBytes,
                                            &Key,
                                            &CompletedOverlapped,
                                            (DWORD)-1);
        if (!Success && (CompletedOverlapped == NULL)) {
            //
            // The call has failed.
            //
            fprintf(stderr,
                    "GetQueuedCompletionStatus on the ReadPort failed, error %d\n",
                    GetLastError());
            exit(1);
        }
        if (!Success) {
            //
            // The call has succeeded, but the initial I/O operation
            // has failed.
            //
            fprintf(stderr,
                    "GetQueuedCompletionStatus on the ReadPort removed a failed\n"
                    "I/O packet, error %d\n",GetLastError());
            //
            // This is probably bad, but what the heck, it's only a demo program,
            // so proceed blindly ahead.
            //
        }

        //
        // Issue the next read using the buffer that has just completed.
        //
        if (ReadPointer.QuadPart < FileSize.QuadPart) {
            Chunk = (PCOPY_CHUNK)CompletedOverlapped;
            Chunk->Overlapped.Offset = ReadPointer.LowPart;
            Chunk->Overlapped.OffsetHigh = ReadPointer.HighPart;
            ReadPointer.QuadPart += BUFFER_SIZE;
            Success = ReadFile(SourceFile,
                               Chunk->Buffer,
                               BUFFER_SIZE,
                               &NumberBytes,
                               &Chunk->Overlapped);

            if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
                fprintf(stderr,
                        "ReadFile at %lx failed, error %d\n",
                        Chunk->Overlapped.Offset,
                        GetLastError());
            }
        }
        else {
            //
            // There are no more reads left to issue, just wait
            // for the pending writes to drain.
            //
            --PendingIO;

        }
    }
}

VOID
WINAPI
WriteLoop(
    ULARGE_INTEGER FileSize
    )
{
    BOOL Success;
    DWORD Key;
    LPOVERLAPPED CompletedOverlapped;
    PCOPY_CHUNK Chunk;
    DWORD NumberBytes;

    while (TRUE) {
        Success = GetQueuedCompletionStatus(ReadPort,
                                            &NumberBytes,
                                            &Key,
                                            &CompletedOverlapped,
                                            (DWORD)-1);
        if (!Success && (CompletedOverlapped == NULL)) {
            //
            // The call has failed.
            //
            fprintf(stderr,
                    "GetQueuedCompletionStatus on the WritePort failed, error %d\n",
                    GetLastError());
            exit(1);
        }
        if (!Success) {
            //
            // The call has succeeded, but the initial I/O operation
            // has failed.
            //
            fprintf(stderr,
                    "GetQueuedCompletionStatus on the WritePort removed a failed\n"
                    "I/O packet, error %d\n", GetLastError());
            //
            // This is probably bad, but what the heck, it's only a demo program,
            // so proceed blindly ahead.
            //
        }

        //
        // Issue the next write using the buffer that has just been read into.
        //
        Chunk = (PCOPY_CHUNK)CompletedOverlapped;

        //
        // Round the number of bytes to write up to a sector boundary
        //
        NumberBytes = (NumberBytes + PageSize - 1) & ~(PageSize-1);

        Success = WriteFile(DestFile,
                            Chunk->Buffer,
                            NumberBytes,
                            &NumberBytes,
                            &Chunk->Overlapped);

        if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
            fprintf(stderr,
                    "WriteFile at %lx failed, error %d\n",
                    Chunk->Overlapped.Offset,
                    GetLastError());
        }
        //
        //Check to see if we've copied the complete file, if so return
        //
        else if (Chunk->Overlapped.InternalHigh >= FileSize.QuadPart)
                    return;

    }

}


README.TXT

Dynamic Data Exchange Manager Library (DDEML)

SUMMARY
=======

CLIENT, CLOCK, DDEINST, DDEMO, DDEPROG, and SERVER are parts of the DDEML
sample application suite.

MORE INFORMATION
================

CLIENT.EXE demonstrates how a client uses the DDEML library.

CLOCK.EXE uses Request or Advise transactions to get the time or the Poke
transaction to change the time.

DDEMO.EXE demonstrates how to use lists of conversations, how to handle
links, how to handle simple asynchronous transactions, and how to use your
own custom formats.

INSTALL.EXE uses DDEML to add groups and items to the Program Manager.

PHTEST.EXE uses PROGHELP.DLL, a simple DDEML service DLL.

SERVER.EXE demonstrates how a DDE server can use enumerable topics and items
to take advantage of application-owned data handles. The server supports
full system topic information and help.


CLIENT.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <ddeml.h>
#include <tchar.h>

#define WINDOWMENU  3   /* position of window menu               */

/* resource ID's */
#define IDCLIENT  1
#define IDCONV    2
#define IDLIST    3

/* menu ID's */

#define IDM_EDITPASTE           2004

#define IDM_CONNECT             3000    // enabled always
#define IDM_RECONNECT           3001    // enabled if list selected
#define IDM_DISCONNECT          3002    // enabled if conversation selected
#define IDM_TRANSACT            3003    // enabled if conversation selected
#define IDM_ABANDON             3004    // enabled if transaction selected
#define IDM_ABANDONALL          3005    // enabled if conv. selected &&
                                        // and any transaction windows exist

#define IDM_BLOCKCURRENT        3010    // enabled if conv. sel.  chkd if conv. blocked
#define IDM_ENABLECURRENT       3011    // enabled if conv. sel.  chkd if not blocked
#define IDM_ENABLEONECURRENT    3012    // enabled if conv. sel.

#define IDM_BLOCKALLCBS         3013    // enabled if any convs.
#define IDM_ENABLEALLCBS        3014    // enabled if any convs.
#define IDM_ENABLEONECB         3015    // enabled if any convs.

#define IDM_BLOCKNEXTCB         3016    // enabled always, chkd if set.
#define IDM_TERMNEXTCB          3017    // enabled if any convs.  chked if set.

#define IDM_TIMEOUT             3021
#define IDM_DELAY               3022
#define IDM_CONTEXT             3023
#define IDM_AUTORECONNECT       3024

#define IDM_WINDOWTILE          4001
#define IDM_WINDOWCASCADE       4002
#define IDM_WINDOWCLOSEALL      4003
#define IDM_WINDOWICONS         4004

#define IDM_XACTTILE            4005
#define IDM_XACTCASCADE         4006
#define IDM_XACTCLOSEALL        4007

#define IDM_WINDOWCHILD         4100

#define IDM_HELP                5001
#define IDM_HELPABOUT           5002

#define DEFTIMEOUT              1000

#include "dialog.h"

// predefined format list item

typedef struct {
    DWORD fmt;
    PTSTR sz;
} FORMATINFO;
#define CFORMATS 3

// conversation (MDI child) window information
typedef struct {
    HWND hwndXaction;       // last xaction window with focus, 0 if none.
    BOOL fList;
    HCONV hConv;
    HSZ hszTopic;
    HSZ hszApp;
    INT x;          // next child coord.
    INT y;
    CONVINFO ci; // most recent status info.
} MYCONVINFO;       // parameters to AddConv() in reverse order.
#define CHILDCBWNDEXTRA     sizeof(HWND)
#define UM_GETNEXTCHILDX    (WM_USER + 200)
#define UM_GETNEXTCHILDY    (WM_USER + 201)
#define UM_DISCONNECTED     (WM_USER + 202)

// transaction processing structure - this structure is associated with
// infoctrl control windows.  A handle to this structure is placed into
// the first window word of the control.
typedef struct {    // used to passinfo to/from TransactionDlgProc and
    DWORD ret;      // TextEntryDlgProc.
    DWORD Result;
    DWORD ulTimeout;
    DWORD wType;
    HCONV hConv;
    HDDEDATA hDdeData;
    DWORD wFmt;
    HSZ hszItem;
    DWORD fsOptions;
} XACT;

typedef struct {
    HDDEDATA hData;
    HSZ hszItem;
    DWORD wFmt;
} OWNED;

// transaction option flags - for fsOptions field and DefOptions global.

#define XOPT_NODATA             0x0001
#define XOPT_ACKREQ             0x0002
#define XOPT_DISABLEFIRST       0x0004
#define XOPT_ABANDONAFTERSTART  0x0008
#define XOPT_BLOCKRESULT        0x0010
#define XOPT_ASYNC              0x0020
#define XOPT_COMPLETED          0x8000      // used internally only.

/* strings */
#define IDS_ILLFNM              1
#define IDS_ADDEXT              2
#define IDS_CLOSESAVE       3
#define IDS_HELPNOTAVAIL    4
#define IDS_CLIENTTITLE     5
#define IDS_APPNAME             6
#define IDS_DDEMLERR        7
#define IDS_BADLENGTH       8

/* attribute flags for DlgDirList */
#define ATTR_DIRS       0xC010          /* find drives and directories */
#define ATTR_FILES      0x0000          /* find ordinary files         */
#define PROP_FILENAME   szPropertyName  /* name of property for dialog */
#define MAX_OWNED   20

/*
 *  GLOBALS
 */
extern CONVCONTEXT CCFilter;
extern DWORD idInst;
extern HANDLE hInst;            /* application instance handle            */
extern HANDLE hAccel;           /* resource handle of accelerators        */
extern HWND hwndFrame;          /* main window handle                     */
extern HWND hwndMDIClient;      /* handle of MDI Client window            */
extern HWND hwndActive;         /* handle of current active MDI child     */
extern HWND hwndActiveEdit;     /* handle of edit control in active child */
extern LONG styleDefault;       /* default child creation state           */
extern DWORD SyncTimeout;
extern LONG DefTimeout;
extern DWORD wDelay;
extern BOOL fEnableCBs;
extern BOOL fEnableOneCB;
extern BOOL fBlockNextCB;
extern BOOL fTermNextCB;
extern BOOL fAutoReconnect;
extern HDDEDATA hDataOwned;
extern DWORD fmtLink;        // registered LINK clipboard fmt
extern DWORD DefOptions;
extern TCHAR szChild[];          /* class of child                         */
extern TCHAR szList[];           /* class of child                         */
extern TCHAR szSearch[];         /* search string                          */
extern TCHAR *szDriver;          /* name of printer driver                 */
extern TCHAR szPropertyName[];   /* filename property for dialog box       */
extern INT iPrinter;            /* level of printing capability           */
extern BOOL fCase;              /* searches case sensitive                */
extern DWORD cFonts;            /* number of fonts enumerated             */
extern FORMATINFO aFormats[];
extern OWNED aOwned[MAX_OWNED];
extern DWORD cOwned;
extern FARPROC lpMsgFilterProc;
extern HSZ hszHuge;
extern HHOOK ghhk;

// MACROS

#ifdef NODEBUG
#define MyAlloc(cb)     (PTSTR)LocalAlloc(LPTR, (cb))
#define MyFree(p)       (LocalUnlock((HANDLE)(p)), LocalFree((HANDLE)(p)))
#else   // DEBUG

#define MyAlloc(cb)     DbgAlloc((DWORD)cb)
#define MyFree(p)       DbgFree((PTSTR)p)
#endif //NODEBUG

/*  externally declared functions
 */

// client.c

BOOL  APIENTRY InitializeApplication(VOID);
BOOL  APIENTRY InitializeInstance(DWORD);
INT FAR cdecl MPError(DWORD,DWORD, ...);
LONG  APIENTRY FrameWndProc(HWND, UINT, WPARAM, LPARAM);
LONG  APIENTRY MDIChildWndProc(HWND, UINT, WPARAM, LPARAM);
HCONV CreateConv(HSZ hszApp, HSZ hszTopic, BOOL fList);
HWND  APIENTRY AddConv(HSZ hszApp, HSZ hszTopic, HCONV hConv, BOOL fList);
PTSTR GetConvListText(HCONVLIST hConvList);
PTSTR GetConvInfoText(HCONV hConv, CONVINFO *pci);
PTSTR GetConvTitleText(HCONV hConv, HSZ hszApp, HSZ hszTopic, BOOL fList);
PTSTR Status2String(DWORD status);
PTSTR State2String(DWORD state);
PTSTR Error2String(DWORD error);
PTSTR Type2String(DWORD wType, DWORD fsOptions);
PTSTR GetHSZName(HSZ hsz);
LRESULT CALLBACK MyMsgFilterProc(int nCode, WPARAM wParam, LPARAM lParam);

// dialog.c

INT FAR DoDialog(LPTSTR lpTemplateName, DLGPROC lpDlgProc, LONG param,
        BOOL fRememberFocus);
BOOL  APIENTRY AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL  APIENTRY ConnectDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL  APIENTRY TransactDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL  APIENTRY AdvOptsDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL  APIENTRY TextEntryDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL  APIENTRY ViewHandleDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL  APIENTRY TimeoutDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL  APIENTRY DelayDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL  APIENTRY ContextDlgProc(HWND, UINT, WPARAM, LPARAM);
VOID Delay(DWORD delay);

// dde.c

BOOL ProcessTransaction(XACT *pxact);
VOID CompleteTransaction(HWND hwndInfoCtr, XACT *pxact);
HDDEDATA CALLBACK DdeCallback(UINT wType, UINT wFmt, HCONV hConv, HSZ hsz1,
        HSZ hsz2, HDDEDATA hData, DWORD lData1, DWORD lData2);
HWND MDIChildFromhConv(HCONV hConv);
HWND FindAdviseChild(HWND hwndMDI, HSZ hszItem, DWORD wFmt);
HWND FindListWindow(HCONVLIST hConvList);
PTSTR GetTextData(HDDEDATA hData);
PTSTR GetFormatData(HDDEDATA hData);
INT MyGetClipboardFormatName(DWORD fmt, LPTSTR lpstr, INT cbMax);
PTSTR GetFormatName(DWORD wFmt);
BOOL MyDisconnect(HCONV hConv);

// mem.c

PTSTR DbgAlloc(DWORD cb);
PTSTR DbgFree(PTSTR p);


CLINIT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  MODULE      : clinit.c                                                 *
 *                                                                         *
 *  PURPOSE     : Contains initialization code for Client                  *
 *                                                                         *
 ***************************************************************************/

#include "client.h"

TCHAR szFrame[] = TEXT("mpframe");   /* Class name for "frame" window */
TCHAR szChild[] = TEXT("mpchild");   /* Class name for MDI window     */
TCHAR szList[] =  TEXT("mplist");    /* Class name for MDI window     */

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : InitializeApplication ()                                   *
 *                                                                          *
 *  PURPOSE    : Sets up the class data structures and does a one-time      *
 *               initialization of the app by registering the window classes*
 *               Also registers the Link clipboard format                   *
 *                                                                          *
 *  RETURNS    : TRUE  - If successful.                                     *
 *               FALSE - otherwise.                                         *
 *                                                                          *
 ****************************************************************************/

BOOL  APIENTRY InitializeApplication()
{
    WNDCLASS    wc;

    fmtLink = RegisterClipboardFormat(TEXT("Link"));

    if (!fmtLink)
        return FALSE;

    /* Register the frame class */
    wc.style         = 0;
    wc.lpfnWndProc   = FrameWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInst;
    wc.hIcon         = LoadIcon(hInst,MAKEINTRESOURCE(IDCLIENT));
    wc.hCursor       = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE+1);
    wc.lpszMenuName  = MAKEINTRESOURCE(IDCLIENT);
    wc.lpszClassName = szFrame;

    if (!RegisterClass (&wc) )
        return FALSE;

    /* Register the MDI child class */
    wc.lpfnWndProc   = MDIChildWndProc;
    wc.hIcon         = LoadIcon(hInst,MAKEINTRESOURCE(IDCONV));
    wc.lpszMenuName  = NULL;
    wc.cbWndExtra    = CHILDCBWNDEXTRA;
    wc.lpszClassName = szChild;

    if (!RegisterClass(&wc))
        return FALSE;

    wc.hIcon         = LoadIcon(hInst, MAKEINTRESOURCE(IDLIST));
    wc.lpszClassName = szList;

    if (!RegisterClass(&wc))
        return FALSE;

    return TRUE;

}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : InitializeInstance ()                                      *
 *                                                                          *
 *  PURPOSE    : Performs a per-instance initialization of Client.          *
 *               - Enlarges message queue to handle lots of DDE messages.   *
 *               - Initializes DDEML for this app                           *
 *               - Creates atoms for our custom formats                     *
 *               - Creates the main frame window                            *
 *               - Loads accelerator table                                  *
 *               - Shows main frame window                                  *
 *                                                                          *
 *  RETURNS    : TRUE  - If initialization was successful.                  *
 *               FALSE - otherwise.                                         *
 *                                                                          *
 ****************************************************************************/
BOOL  APIENTRY InitializeInstance(
DWORD nCmdShow)
{
    extern HWND  hwndMDIClient;
    TCHAR         sz[80];
    INT          i;

    if (DdeInitialize(&idInst, (PFNCALLBACK)MakeProcInstance(
            (FARPROC)DdeCallback, hInst), APPCMD_CLIENTONLY, 0L))
        return FALSE;

    CCFilter.iCodePage = CP_WINANSI;

    for (i = 0; i < CFORMATS; i++) {
        if (aFormats[i].fmt == 0)
            aFormats[i].fmt = RegisterClipboardFormat(aFormats[i].sz);
    }
    hszHuge = DdeCreateStringHandle(idInst, TEXT("Huge"), 0);

    /* Get the base window title */
    LoadString(hInst, IDS_APPNAME, sz, sizeof(sz));

    /* Create the frame */
    hwndFrame = CreateWindow (szFrame,
                              sz,
                              WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              400,
                              200,
                              NULL,
                              NULL,
                              hInst,
                              NULL);

    if (!hwndFrame || !hwndMDIClient)
        return FALSE;

    /* Load main menu accelerators */
    if (!(hAccel = LoadAccelerators (hInst, MAKEINTRESOURCE(IDCLIENT))))
        return FALSE;

    /* Display the frame window */
    ShowWindow (hwndFrame, nCmdShow);
    UpdateWindow (hwndFrame);

    /*
     * We set this hook up so that we can catch the MSGF_DDEMGR filter
     * which is called when DDEML is in a modal loop during synchronous
     * transaction processing.
     */
    lpMsgFilterProc = (FARPROC)MakeProcInstance((FARPROC)MyMsgFilterProc, hInst);
    ghhk = SetWindowsHookEx(WH_MSGFILTER, (HOOKPROC)lpMsgFilterProc, NULL,
            GetCurrentThreadId());

    return TRUE;
}


DDE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  MODULE      : dde.c                                                    *
 *                                                                         *
 *  PURPOSE     : Contains routines for handling of DDE interaction with   *
 *                DDEML.                                                   *
 *                                                                         *
 ***************************************************************************/
#include "client.h"
#include <string.h>
#include <memory.h>
#include "infoctrl.h"
#include "huge.h"

TCHAR szT[100];

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : CreateXactionWindow()                                      *
 *                                                                          *
 *  PURPOSE    : Creates a transaction window for the given transaction     *
 *               under the given conversation window.                       *
 *                                                                          *
 *  RETURNS    : TRUE  - If successful.                                     *
 *               FALSE - otherwise.                                         *
 *                                                                          *
 ****************************************************************************/
HWND CreateXactionWindow(
HWND hwndMDI,
XACT *pxact)
{
    PTSTR pszFmt, pszItem;
    PTSTR pData;
    HWND hwnd;

    pszItem = GetHSZName(pxact->hszItem);
    pszFmt = GetFormatName(pxact->wFmt);
    pData = GetTextData(pxact->hDdeData);

    /*
     *    type/opts        ITEM                ret      GWL_USER=pxact
     *
     *
     *
     *
     *                     DATA
     *
     *
     *
     *
     *    state/error      FORMAT           Result
     */
    hwnd = CreateInfoCtrl((LPTSTR)pData,
            (INT)SendMessage(hwndMDI, UM_GETNEXTCHILDX, 0, 0L),
            (INT)SendMessage(hwndMDI, UM_GETNEXTCHILDY, 0, 0L),
            200, 100, hwndMDI, hInst,
            Type2String(pxact->wType, pxact->fsOptions), pszItem, NULL,
            TEXT("Starting"), (LPTSTR)pszFmt, NULL,
            ICSTY_SHOWFOCUS, 0, (DWORD)(LPTSTR)pxact);
    MyFree(pszItem);
    MyFree(pszFmt);
    MyFree(pData);
    return(hwnd);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : ProcessTransaction()                                       *
 *                                                                          *
 *  PURPOSE    : Processes synchronous transactions entirely and starts     *
 *               async transactions.  Transaction attempts result in a      *
 *               transaction window being created which displays the state  *
 *               or results of the transaction.  (the callback function     *
 *               updates these windows as it gets calls) Transaction        *
 *               windows stay around until abandoned by the user or until   *
 *               the conversation is disconnected.  Advise Data and Advise  *
 *               Stop transactions are special.  We don't create a new      *
 *               window if the associated advise start transaction window   *
 *               can be found.                                              *
 *                                                                          *
 *  RETURNS    : TRUE  - If successful.                                     *
 *               FALSE - otherwise.                                         *
 *                                                                          *
 ****************************************************************************/
BOOL ProcessTransaction(
XACT *pxact)
{
    CONVINFO ci;
    HWND hwndInfoCtrl = 0;

    /* create transaction window to show we tried (except in ADVSTOP case) */

    pxact = (XACT *)memcpy(MyAlloc(sizeof(XACT)), (PTSTR)pxact, sizeof(XACT));
    ci.cb = sizeof(CONVINFO);
    DdeQueryConvInfo(pxact->hConv, QID_SYNC, &ci); // ci.hUser==hConv
    if (pxact->wType == XTYP_ADVSTOP) {
        hwndInfoCtrl = FindAdviseChild((HWND)ci.hUser, pxact->hszItem,
                pxact->wFmt);
        if (hwndInfoCtrl) {
            SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_UL,
                    (DWORD)(LPTSTR)Type2String(pxact->wType, pxact->fsOptions));
            DdeFreeStringHandle(idInst, pxact->hszItem);
        }
    }
    /*
     * If we still need to create a transaction window, do so here.
     */
    if (!hwndInfoCtrl) {
        hwndInfoCtrl = CreateXactionWindow((HWND)ci.hUser, pxact);
        if (!hwndInfoCtrl) {
            MyFree(pxact);
            return 0;
        }
        SetFocus(hwndInfoCtrl);
    }
    /*
     * Disable callbacks for this conversation now if the XOPT_DISABLEFIRST
     * option is set.  This tests disabling asynchronous transactions
     * before they are completed.
     */
    if (pxact->fsOptions & XOPT_DISABLEFIRST)
        DdeEnableCallback(idInst, pxact->hConv, EC_DISABLE);
    /*
     * Adjust the timeout for asynchronous transactions.
     */
    if (pxact->fsOptions & XOPT_ASYNC)
        pxact->ulTimeout = TIMEOUT_ASYNC;

    /*
     * start transaction with DDEML here
     */
    pxact->ret = (DWORD) DdeClientTransaction((LPBYTE)pxact->hDdeData, 0xFFFFFFFF,
            pxact->hConv, pxact->hszItem, pxact->wFmt,
            pxact->wType,
            pxact->ulTimeout, (LPDWORD)&pxact->Result);

    /*
     * show return value in transaction window
     */
    wsprintf(szT, TEXT("ret=%lx"), pxact->ret);
    SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_UR, (DWORD)(LPTSTR)szT);

    /*
     * show result or ID value in transaction window
     */
    wsprintf(szT, pxact->fsOptions & XOPT_ASYNC ? TEXT("ID=%ld") :
            TEXT("result=0x%lx"), pxact->Result);
    SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LR, (DWORD)(LPTSTR)szT);

    if ((pxact->fsOptions & XOPT_ASYNC) && pxact->ret) {
        /*
         * asynchronous successful start - link transaction to window.
         */
        DdeSetUserHandle(pxact->hConv, pxact->Result, (DWORD)hwndInfoCtrl);

        /*
         * Abandon started async transaction after initiated if
         * XOPT_ABANDONAFTERSTART is chosen.  This tests the mid-transaction
         * abandoning code.
         */
        if (pxact->fsOptions & XOPT_ABANDONAFTERSTART)
            DdeAbandonTransaction(idInst, pxact->hConv, pxact->Result);
        /*
         * show actual status
         */
        ci.cb = sizeof(CONVINFO);
        DdeQueryConvInfo(pxact->hConv, pxact->Result, &ci);
        SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LL,
                (DWORD)(LPTSTR)State2String(ci.wConvst));
    } else {
        /*
         * Synchronous transactions are completed already so pass on to
         * CompleteTransaction right away.
         */
        CompleteTransaction(hwndInfoCtrl, pxact);
    }
    return TRUE;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : CompleteTransaction()                                      *
 *                                                                          *
 *  PURPOSE    : This handles completed synchronous and asynchronous        *
 *               transactions as well as failed attempted transactions.     *
 *                                                                          *
 *  RETURNS    : TRUE  - If successful.                                     *
 *               FALSE - otherwise.                                         *
 *                                                                          *
 ****************************************************************************/
VOID CompleteTransaction(
HWND hwndInfoCtrl,
XACT *pxact)
{
    PTSTR psz;

    if (pxact->ret) {
        /*
         * Successful transaction case
         */
        SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LL,
                (DWORD)(LPTSTR)TEXT("Completed"));

        if (pxact->wType == XTYP_REQUEST) {
            /*
             * Show resulting data
             */
            psz = GetTextData((HDDEDATA)pxact->ret);
            if (!DdeCmpStringHandles(pxact->hszItem, hszHuge) &&
                    !CheckHugeData((HDDEDATA)pxact->ret)) {
                SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_CENTER,
                        (DWORD)(LPTSTR)TEXT("Invalid Huge Data"));
            } else {
                SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_CENTER,
                        (DWORD)(LPTSTR)psz);
            }
            MyFree(psz);
            /*
             * free returned data since it is displayed.
             */
            DdeFreeDataHandle((HDDEDATA)pxact->ret);
            pxact->ret = 0L;
            SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_UR, 0);
        }
    } else {
        /*
         * failed - show error result.
         */
        SendMessage(hwndInfoCtrl, ICM_SETSTRING, ICSID_LL,
                (DWORD)(LPTSTR)Error2String(DdeGetLastError(idInst)));
    }
    pxact->fsOptions |= XOPT_COMPLETED;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : DdeCallback()                                              *
 *                                                                          *
 *  PURPOSE    : This handles all callbacks from the DDEML.  This handles   *
 *               updating of the associated conversation and any special    *
 *               testing cases such as blocking callbacks etc.              *
 *                                                                          *
 *               For the most part, clients only handle advise data and     *
 *               asynchronous transaction completion here.                  *
 *                                                                          *
 *  RETURNS    : Results vary depending on transaction type.                *
 *                                                                          *
 ****************************************************************************/
HDDEDATA CALLBACK DdeCallback(
UINT wType,
UINT wFmt,
HCONV hConv,
HSZ hsz1,
HSZ hsz2,
HDDEDATA hData,
DWORD lData1,
DWORD lData2)
{
    HWND hwnd;
    CONVINFO ci;
    XACT *pxact;

    if (hConv) {
        /*
         * update conversation status if it changed.
         */
        MYCONVINFO *pmci;

        ci.cb = sizeof(CONVINFO);
        if (!DdeQueryConvInfo(hConv, QID_SYNC, &ci) || (!IsWindow((HWND)ci.hUser))) {
            /*
             * This conversation does not yet have a corresponding MDI window
             * or is disconnected.
             */
            return 0;
        }
        if (pmci = (MYCONVINFO *)GetWindowLong((HWND)ci.hUser, 0)) {
            if (pmci->ci.wStatus != ci.wStatus ||
                    pmci->ci.wConvst != ci.wConvst ||
                    pmci->ci.wLastError != ci.wLastError) {
                /*
                 * Things have changed, updated the conversation window.
                 */
                InvalidateRect((HWND)ci.hUser, NULL, TRUE);
            }
            if (ci.wConvst & ST_INLIST) {
                /*
                 * update the associated list window (if any) as well.
                 */
                if (hwnd = FindListWindow(ci.hConvList))
                    InvalidateRect(hwnd, NULL, TRUE);
            }
        }
    }

    /*
     * handle special block on next callback option here.  This demonstrates
     * the CBR_BLOCK feature.
     */
    if (fBlockNextCB && !(wType & XTYPF_NOBLOCK)) {
        fBlockNextCB = FALSE;
        return(CBR_BLOCK);
    }

    /*
     * handle special termination here.  This demonstrates that at any time
     * a client can drop a conversation.
     */
    if (fTermNextCB && hConv && wType != XTYP_DISCONNECT) {
        fTermNextCB = FALSE;
        MyDisconnect(hConv);
        return(0);
    }

    /*
     * Now we begin sort out what to do.
     */
    switch (wType) {
    case XTYP_REGISTER:
    case XTYP_UNREGISTER:
        /*
         * This is where the client would insert code to keep track of
         * what servers are available.  This could cause the initiation
         * of some conversations.
         */
        break;

    case XTYP_DISCONNECT:
        if (fAutoReconnect) {
            /*
             * attempt a reconnection
             */
            if (hConv = DdeReconnect(hConv)) {
                AddConv(ci.hszServiceReq, ci.hszTopic, hConv, FALSE);
                return 0;
            }
        }

        /*
         * update conv window to show its new state.
         */
        SendMessage((HWND)ci.hUser, UM_DISCONNECTED, 0, 0);
        return 0;
        break;

    case XTYP_ADVDATA:
        /*
         * data from an active advise loop (from a server)
         */
        Delay(wDelay);
        hwnd = FindAdviseChild((HWND)ci.hUser, hsz2, wFmt);
        if (!IsWindow(hwnd)) {
            PTSTR pszItem, pszFmt;
            /*
             * AdviseStart window is gone, make a new one.
             */
            pxact = (XACT *)MyAlloc(sizeof(XACT));
            pxact->wType = wType;
            pxact->hConv = hConv;
            pxact->wFmt = wFmt;
            pxact->hszItem = hsz2;
            DdeKeepStringHandle(idInst, hsz2);

            pszItem = GetHSZName(hsz2);
            pszFmt = GetFormatName(wFmt);

            hwnd = CreateInfoCtrl(NULL,
                    (INT)SendMessage((HWND)ci.hUser, UM_GETNEXTCHILDX, 0, 0L),
                    (INT)SendMessage((HWND)ci.hUser, UM_GETNEXTCHILDY, 0, 0L),
                    200, 100,
                    (HWND)ci.hUser, hInst,
                    Type2String(wType, 0), (LPTSTR)pszItem, NULL,
                    NULL, (LPTSTR)pszFmt, NULL,
                    ICSTY_SHOWFOCUS, 0, (DWORD)(LPTSTR)pxact);

            MyFree(pszFmt);
            MyFree(pszItem);

            if (!IsWindow(hwnd))
                return(DDE_FNOTPROCESSED);
        }
        if (!hData) {
            /*
             * XTYPF_NODATA case - request the info. (we do this synchronously
             * for simplicity)
             */
            hData = DdeClientTransaction(NULL, 0L, hConv, hsz2, wFmt,
                    XTYP_REQUEST, DefTimeout, NULL);
        }
        if (hData) {
            PTSTR pData;
            /*
             * Show incomming data on corresponding transaction window.
             */
            pData = GetTextData(hData);
            SendMessage(hwnd, ICM_SETSTRING, ICSID_CENTER, (DWORD)(LPTSTR)pData);
            MyFree(pData);
            DdeFreeDataHandle(hData);
        }
        SendMessage(hwnd, ICM_SETSTRING, ICSID_LL, (DWORD)(LPTSTR)TEXT("Advised"));
        return((HDDEDATA)DDE_FACK);
        break;

    case XTYP_XACT_COMPLETE:
        /*
         * An asynchronous transaction has completed.  Show the results.
         *
         * ...unless the XOPT_BLOCKRESULT is chosen.
         */

        ci.cb = sizeof(CONVINFO);
        if (DdeQueryConvInfo(hConv, lData1, &ci) &&
                IsWindow((HWND)ci.hUser) &&
                (pxact = (XACT *)GetWindowLong((HWND)ci.hUser, GWL_USER))) {

            if (pxact->fsOptions & XOPT_BLOCKRESULT) {
                pxact->fsOptions &= ~XOPT_BLOCKRESULT;
                return(CBR_BLOCK);
            }

            pxact->Result = lData2;
            pxact->ret = (DWORD)hData;
            CompleteTransaction((HWND)ci.hUser, pxact);
        }
        break;
    }
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : FindAdviseChild()                                          *
 *                                                                          *
 *  PURPOSE    : Search through the child windows of hwndMDI for an info    *
 *               ctrl that has the same Item and format and is an           *
 *               ADVSTART ADVSTOP or ADVDATA transaction window.            *
 *                                                                          *
 *               We use these to show the associated advise data.           *
 *                                                                          *
 *  RETURNS    : The transaction window handle or 0 on failure.             *
 *                                                                          *
 ****************************************************************************/
HWND FindAdviseChild(
HWND hwndMDI,
HSZ hszItem,
DWORD wFmt)
{
    HWND hwnd, hwndStart;
    XACT *pxact;

    if (!IsWindow(hwndMDI))
        return 0;

    hwnd = hwndStart = GetWindow(hwndMDI, GW_CHILD);
    while (hwnd && IsChild(hwndMDI, hwnd)) {
        pxact = (XACT *)GetWindowLong(hwnd, GWL_USER);
        if (pxact &&
                (pxact)->wFmt == wFmt &&
                (pxact)->hszItem == hszItem &&
                (
                    ((pxact->wType & XTYP_ADVSTART) == XTYP_ADVSTART) ||
                    (pxact->wType == XTYP_ADVSTOP) ||
                    (pxact->wType == XTYP_ADVDATA)
                )
           ) {
            return(hwnd);
        }
        hwnd = GetWindow(hwnd, GW_HWNDNEXT);
        if (hwnd == hwndStart)
            return 0;
    }
    return 0;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : FindListWindow()                                           *
 *                                                                          *
 *  PURPOSE    : Locates the list window associated with this conversation  *
 *               list.                                                      *
 *                                                                          *
 *  RETURNS    : The window handle of the list window or 0 on failure.      *
 *                                                                          *
 ****************************************************************************/
HWND FindListWindow(
HCONVLIST hConvList)
{
    HWND hwnd;
    MYCONVINFO *pmci;

    hwnd = GetWindow(hwndMDIClient, GW_CHILD);
    while (hwnd) {
        if (GetWindowLong(hwnd, GWL_WNDPROC) == (LONG)MDIChildWndProc) {
            pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0);
            if (pmci != NULL && pmci->fList &&
                    pmci->hConv == (HCONV)hConvList)
                return(hwnd);
        }
        hwnd = GetWindow(hwnd, GW_HWNDNEXT);
    }
    return 0;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : GetTextData()                                              *
 *                                                                          *
 *  PURPOSE    : Allocates and returns a pointer to the data contained in   *
 *               hData.  This assumes that hData points to text data and    *
 *               will properly handle huge text data by leaving out the     *
 *               middle of the string and placing the size of the string    *
 *               into this string portion.                                  *
 *                                                                          *
 *  RETURNS    : A pointer to the allocated string.                         *
 *                                                                          *
 ****************************************************************************/
PTSTR GetTextData(
HDDEDATA hData)
{
    PTSTR psz;
    DWORD cb;

#define MAXCCH  1024

    if (hData == 0) {
        return(NULL);
    }

    cb = DdeGetData(hData, NULL, 0, 0);
    if (!hData || !cb)
        return NULL;

    if (cb > MAXCCH) {                // possibly HUGE object!
        psz = MyAlloc(MAXCCH * sizeof(TCHAR));
        DdeGetData(hData, (PBYTE)psz, MAXCCH - 46 * sizeof(TCHAR), 0L);
        wsprintf(&psz[MAXCCH - 46], TEXT("<---Size=%ld"), cb);
    } else {
        psz = MyAlloc((DWORD)cb);
        DdeGetData(hData, (LPBYTE)psz, cb, 0L);
    }
    return psz;
#undef MAXCCH
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : MyGetClipboardFormatName()                                 *
 *                                                                          *
 *  PURPOSE    : Properly retrieves the string associated with a clipboard  *
 *               format.  If the format does not have a string associated   *
 *               with it, the string #dddd is returned.                     *
 *                                                                          *
 *  RETURNS    : The number of characters copied into lpstr or 0 on error.  *
 *                                                                          *
 ****************************************************************************/
INT MyGetClipboardFormatName(
DWORD fmt,
LPTSTR lpstr,
INT cbMax)
{
    if (fmt < 0xc000) {
        // predefined or integer format - just get the atom string
        // wierdly enough, GetClipboardFormatName() doesn't support this.
        return(GlobalGetAtomName((ATOM)fmt, lpstr, cbMax));
    } else {
        return(GetClipboardFormatName(fmt, lpstr, cbMax));
    }
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : GetFormatName()                                            *
 *                                                                          *
 *  PURPOSE    : allocates and returns a pointer to a string representing   *
 *               a format.  Use MyFree() to free this string.               *
 *                                                                          *
 *  RETURNS    : The number of characters copied into lpstr or 0 on error.  *
 *                                                                          *
 ****************************************************************************/
PTSTR GetFormatName(
DWORD wFmt)
{
    PTSTR psz;
    DWORD cb;

    if (wFmt == CF_UNICODETEXT) {
       psz = MyAlloc(40);
       _tcscpy(psz, TEXT("CF_UNICODETEXT"));
       return psz;
    }
    if (wFmt == CF_TEXT) {
        psz = MyAlloc(40);
        _tcscpy(psz, TEXT("CF_TEXT"));
        return psz;
    }
    psz = MyAlloc(255);
    *psz = TEXT('\0');
    cb = GetClipboardFormatName(wFmt, psz, 255) + 1;
    return((PTSTR)LocalReAlloc((HANDLE)psz, cb, LMEM_MOVEABLE));
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : MyDisconnect()                                             *
 *                                                                          *
 *  PURPOSE    : Disconnects the given conversation after updating the      *
 *               associated conversation window.                            *
 *                                                                          *
 *  RETURNS    : TRUE on success, FALSE on failuer.                         *
 *                                                                          *
 ****************************************************************************/
BOOL MyDisconnect(
HCONV hConv)
{
    CONVINFO ci;
    HWND hwnd;
    // before we disconnect, invalidate the associated list window - if
    // applicable.

    ci.cb = sizeof(CONVINFO);

    if (DdeQueryConvInfo(hConv, QID_SYNC, &ci) && ci.hConvList &&
            (hwnd = FindListWindow(ci.hConvList)))
        InvalidateRect(hwnd, NULL, TRUE);
    return(DdeDisconnect(hConv));
}


DDEMLCL.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  PROGRAM     : client.c                                                 *
 *                                                                         *
 *  PURPOSE     : To demonstrate how to use the DDEML library from the     *
 *                client side and for basic testing of the DDEML API.      *
 *                                                                         *
 ***************************************************************************/

#include "client.h"
#include <string.h>
#include <memory.h>
#include "infoctrl.h"

/* global variables used in this module or among more than one module */
CONVCONTEXT CCFilter = { sizeof(CONVCONTEXT), 0, 0, 0, 0L, 0L,
    {
        sizeof(SECURITY_QUALITY_OF_SERVICE),
        SecurityImpersonation,
        SECURITY_STATIC_TRACKING,
        TRUE
    }
};
DWORD idInst = 0;
HANDLE hInst;                       /* Program instance handle               */
HANDLE hAccel;                      /* Main accelerator resource             */
HWND hwndFrame           = NULL;    /* Handle to main window                 */
HWND hwndMDIClient       = NULL;    /* Handle to MDI client                  */
HWND hwndActive          = NULL;    /* Handle to currently activated child   */
LONG DefTimeout      = DEFTIMEOUT;  /* default synchronous transaction timeout */
DWORD wDelay = 0;
BOOL fBlockNextCB = FALSE;     /* set if next callback causes a CBR_BLOCK    */
BOOL fTermNextCB = FALSE;      /* set to call DdeDisconnect() on next callback */
BOOL fAutoReconnect = FALSE;   /* set if DdeReconnect() is to be called on XTYP_DISCONNECT callbacks */
HDDEDATA hDataOwned = 0;       /* Current owned huge data handle             */
DWORD fmtLink = 0;                  /* link clipboard format number          */
DWORD DefOptions = 0;               /* default transaction optons            */
OWNED aOwned[MAX_OWNED];            /* list of all owned handles.            */
DWORD cOwned = 0;                   /* number of existing owned handles.     */
FARPROC lpMsgFilterProc;            /* instance proc from MSGF_DDEMGR filter */
HSZ hszHuge;                        /* used for checking huge item data */
HHOOK ghhk = 0;

/*
 * This is the array of formats we support
 */
FORMATINFO aFormats[] = {
#ifdef UNICODE
    { CF_UNICODETEXT, TEXT("CF_UNICODETEXT") },       // exception!  predefined format
#else
    { CF_TEXT, TEXT("CF_TEXT") },       // exception!  predefined format
#endif
    { 0, TEXT("Dummy1")  },
    { 0, TEXT("Dummy2")  },
};

/* Forward declarations of helper functions in this module */
VOID NEAR PASCAL CloseAllChildren(VOID);
VOID NEAR PASCAL InitializeMenu (HANDLE);
VOID NEAR PASCAL CommandHandler (HWND,DWORD);
VOID NEAR PASCAL SetWrap (HWND,BOOL);

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : WinMain(HANDLE, HANDLE, LPTSTR, int)                        *
 *                                                                          *
 *  PURPOSE    : Creates the "frame" window, does some initialization and   *
 *               enters the message loop.                                   *
 *                                                                          *
 ****************************************************************************/
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR  lpszCmdLine,
INT    nCmdShow)
{
    MSG msg;

    hInst = hInstance;

    /* If this is the first instance of the app. register window classes */
    if (!hPrevInstance){
        if (!InitializeApplication ())
            return 0;
    }

    /* Create the frame and do other initialization */
    if (!InitializeInstance(nCmdShow))
        return 0;

    /* Enter main message loop */
    while (GetMessage (&msg, NULL, 0, 0)){
        ((HOOKPROC)*lpMsgFilterProc)(MSGF_DDEMGR, 0, (LONG)(LPMSG)&msg);
    }

    // free up any appowned handles
    while (cOwned) {
        DdeFreeDataHandle(aOwned[--cOwned].hData);
    }
    DdeFreeStringHandle(idInst, hszHuge);
    DdeUninitialize(idInst);

    UnhookWindowsHook(WH_MSGFILTER, (HOOKPROC)lpMsgFilterProc);
    FreeProcInstance(lpMsgFilterProc);

    return 0;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : FrameWndProc (hwnd, msg, wParam, lParam )                  *
 *                                                                          *
 *  PURPOSE    : The window function for the "frame" window, which controls *
 *               the menu and encompasses all the MDI child windows. Does   *
 *               the major part of the message processing. Specifically, in *
 *               response to:                                               *
 *                                                                          *
 ****************************************************************************/
LONG  APIENTRY FrameWndProc ( hwnd, msg, wParam, lParam )
HWND   hwnd;
UINT   msg;
WPARAM wParam;
LPARAM lParam;

{
    switch (msg){
        case WM_CREATE:{
            CLIENTCREATESTRUCT ccs;

            /* Find window menu where children will be listed */
            ccs.hWindowMenu = GetSubMenu (GetMenu(hwnd),WINDOWMENU);
            ccs.idFirstChild = IDM_WINDOWCHILD;

            /* Create the MDI client filling the client area */
            hwndMDIClient = CreateWindow (TEXT("mdiclient"),
                                          NULL,
                                          WS_CHILD | WS_CLIPCHILDREN |
                                          WS_VSCROLL | WS_HSCROLL,
                                          0,
                                          0,
                                          0,
                                          0,
                                          hwnd,
                                          (HMENU)0xCAC,
                                          hInst,
                                          (LPTSTR)&ccs);

            ShowWindow (hwndMDIClient,SW_SHOW);
            break;
        }

        case WM_INITMENU:
            InitializeMenu ((HMENU)wParam);
            break;

        case WM_COMMAND:
            CommandHandler (hwnd, LOWORD(wParam));
            break;

        case WM_CLOSE:
            CloseAllChildren();
            DestroyWindow(hwnd);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            /*  use DefFrameProc() instead of DefWindowProc() since there
             *  are things that have to be handled differently because of MDI
             */
            return DefFrameProc (hwnd,hwndMDIClient,msg,wParam,lParam);
    }
    return 0;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : MDIChildWndProc                                            *
 *                                                                          *
 *  PURPOSE    : The window function for the "child" conversation and list  *
 *               windows.                                                   *
 *                                                                          *
 ****************************************************************************/
LONG  APIENTRY MDIChildWndProc( hwnd, msg, wParam, lParam )
HWND   hwnd;
UINT   msg;
WPARAM wParam;
LPARAM lParam;
{
    MYCONVINFO *pmci;
    RECT rc;

    switch (msg){
    case WM_CREATE:
        /*
         * Create a coresponding conversation info structure to link this
         * window to the conversation or conversation list it represents.
         *
         * lParam: points to the conversation info to initialize our copy to.
         */
        pmci = (MYCONVINFO *)MyAlloc(sizeof(MYCONVINFO));
        if (pmci != NULL) {
            memcpy(pmci,
                    (LPTSTR)((LPMDICREATESTRUCT)((LPCREATESTRUCT)lParam)->lpCreateParams)->lParam,
                    sizeof(MYCONVINFO));
            pmci->hwndXaction = 0;              /* no current transaction yet */
            pmci->x = pmci->y = 0;              /* new transaction windows start here */
            DdeKeepStringHandle(idInst, pmci->hszTopic);/* keep copies of the hszs for us */
            DdeKeepStringHandle(idInst, pmci->hszApp);

             // link hConv and hwnd together
            SetWindowLong(hwnd, 0, (DWORD)pmci);

            /*
             * non-list windows link the conversations to the windows via the
             * conversation user handle.
             */
            if (!pmci->fList)
                DdeSetUserHandle(pmci->hConv, QID_SYNC, (DWORD)hwnd);
        }
        goto CallDCP;
        break;

    case UM_GETNEXTCHILDX:
    case UM_GETNEXTCHILDY:
        /*
         * Calculate the next place to put the next transaction window.
         */
        {
            pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0);
            GetClientRect(hwnd, &rc);
            if (msg == UM_GETNEXTCHILDX) {
                pmci->x += 14;
                if (pmci->x > (rc.right - 200 - rc.left))
                    pmci->x = 0;
                return(pmci->x);
            } else {
                pmci->y += 12;
                if (pmci->y > (rc.bottom - 100 - rc.top))
                    pmci->y = 0;
                return(pmci->y);
            }
        }
        break;

    case UM_DISCONNECTED:
        /*
         * Disconnected conversations can't have any transactions so we
         * remove all the transaction windows here to show whats up.
         */
        {
            HWND hwndT;
            while (hwndT = GetWindow(hwnd, GW_CHILD))
                DestroyWindow(hwndT);
            InvalidateRect(hwnd, NULL, TRUE);
        }
        break;

    case WM_DESTROY:
        /*
         * Cleanup our conversation info structure, and disconnect all
         * conversations associated with this window.
         */
        pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0);
        pmci->hwndXaction = 0;      /* clear this to avoid focus problems */
        if (pmci->hConv) {
            if (pmci->fList) {
                DdeDisconnectList((HCONVLIST)pmci->hConv);
            } else {
                MyDisconnect(pmci->hConv);
            }
        }
        DdeFreeStringHandle(idInst, pmci->hszTopic);
        DdeFreeStringHandle(idInst, pmci->hszApp);
        MyFree(pmci);
        goto CallDCP;
        break;

    case WM_SETFOCUS:
        /*
         * This catches focus changes caused by dialogs.
         */
        lParam = (LPARAM)hwnd;
        // fall through

    case WM_MDIACTIVATE:
        hwndActive = (HWND)(lParam);
        pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0);
        /*
         * pass the focus onto the current transaction window.
         */
        if ((lParam == (LONG)hwnd) &&
                IsWindow(pmci->hwndXaction))
            SetFocus(pmci->hwndXaction);
        break;

    case ICN_HASFOCUS:
        /*
         * update which transaction window is the main one.
         */
        pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0);
        pmci->hwndXaction = wParam ? (HWND)lParam : NULL;
        break;

    case ICN_BYEBYE:
        /*
         * Transaction window is closing...
         *
         * wParam = hwndXact
         * lParam = lpxact
         */
        pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0);
        if (pmci != NULL) {
            XACT *pxact;

            pxact = (XACT *)lParam;
            if (pxact != NULL) {
                /*
                 * If this transaction is active, abandon it first.
                 */
                if (pxact->fsOptions & XOPT_ASYNC &&
                        !(pxact->fsOptions & XOPT_COMPLETED)) {
                    DdeAbandonTransaction(idInst, pmci->hConv, pxact->Result);
                }
                /*
                 * release resources associated with transaction.
                 */
                DdeFreeStringHandle(idInst, pxact->hszItem);
                MyFree((PTSTR)pxact);
                /*
                 * Locate next apropriate transaction window to get focus.
                 */
                if (!pmci->hwndXaction || pmci->hwndXaction == (HWND)wParam)
                    pmci->hwndXaction = GetWindow(hwnd, GW_CHILD);
                if (pmci->hwndXaction == (HWND)wParam)
                    pmci->hwndXaction = GetWindow((HWND)wParam, GW_HWNDNEXT);
                if (pmci->hwndXaction == (HWND)wParam ||
                        !IsWindow(pmci->hwndXaction) ||
                        !IsChild(hwnd, pmci->hwndXaction))
                    pmci->hwndXaction = NULL;
                else
                    SetFocus(pmci->hwndXaction);
            }
        }
        break;

    case WM_PAINT:
        /*
         * Paint this conversation's related information.
         */
        pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0);
        {
            PAINTSTRUCT ps;
            PTSTR psz;

            BeginPaint(hwnd, &ps);
            SetBkMode(ps.hdc, TRANSPARENT);
            psz = pmci->fList ? GetConvListText((HCONVLIST)pmci->hConv) :
                    GetConvInfoText(pmci->hConv, &pmci->ci);
            if (psz) {
                GetClientRect(hwnd, &rc);
                DrawText(ps.hdc, psz, -1, &rc,
                        DT_WORDBREAK | DT_LEFT | DT_NOPREFIX | DT_TABSTOP);
                MyFree(psz);
            }
            EndPaint(hwnd, &ps);
        }
        break;

    case WM_QUERYENDSESSION:
        return TRUE;

    default:
CallDCP:
        /* Again, since the MDI default behaviour is a little different,
         * call DefMDIChildProc instead of DefWindowProc()
         */
        return DefMDIChildProc (hwnd, msg, wParam, lParam);
    }
    return FALSE;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : Initializemenu ( hMenu )                                   *
 *                                                                          *
 *  PURPOSE    : Sets up greying, enabling and checking of main menu items  *
 *               based on the app's state.                                  *
 *                                                                          *
 ****************************************************************************/
VOID NEAR PASCAL InitializeMenu ( hmenu )
HANDLE hmenu;
{
    BOOL fLink      = FALSE; // set if Link format is on the clipboard;
    BOOL fAny       = FALSE; // set if hwndActive exists
    BOOL fList      = FALSE; // set if hwndActive is a list window
    BOOL fConnected = FALSE; // set if hwndActive is a connection conversation.
    BOOL fXaction   = FALSE; // set if hwndActive has a selected transaction window
    BOOL fXactions  = FALSE; // set if hwndActive contains transaction windows
    BOOL fBlocked   = FALSE; // set if hwndActive conversation is blocked.
    BOOL fBlockNext = FALSE; // set if handActive conversation is blockNext.
    MYCONVINFO *pmci = NULL;

    if (OpenClipboard(hwndFrame)) {
        fLink = (IsClipboardFormatAvailable(fmtLink));
        CloseClipboard();
    }

    if (fAny = (IsWindow(hwndActive) &&
            (pmci = (MYCONVINFO *)GetWindowLong(hwndActive, 0)))) {
        fXactions = (BOOL)GetWindow(hwndActive, GW_CHILD);
        if (!(fList = pmci->fList)) {
            CONVINFO ci;

            ci.cb = sizeof(CONVINFO);
            DdeQueryConvInfo(pmci->hConv, QID_SYNC, &ci);
            fConnected = ci.wStatus & ST_CONNECTED;
            fXaction = IsWindow(pmci->hwndXaction);
            fBlocked = ci.wStatus & ST_BLOCKED;
            fBlockNext = ci.wStatus & ST_BLOCKNEXT;
        }
    }

    EnableMenuItem(hmenu,   IDM_EDITPASTE,
            fLink           ? MF_ENABLED    : MF_GRAYED);

    // IDM_CONNECTED - always enabled.

    EnableMenuItem(hmenu,   IDM_RECONNECT,
            fList           ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem (hmenu,  IDM_DISCONNECT,
            fConnected && !(fXaction || fXactions) ? MF_ENABLED : MF_GRAYED);

    EnableMenuItem (hmenu,  IDM_TRANSACT,
            fConnected      ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem(hmenu,   IDM_ABANDON,
            fXaction        ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem(hmenu,   IDM_ABANDONALL,
            fXactions ? MF_ENABLED : MF_GRAYED);

    EnableMenuItem (hmenu,  IDM_BLOCKCURRENT,
            fConnected && !fBlocked ? MF_ENABLED    : MF_GRAYED);
    CheckMenuItem(hmenu, IDM_BLOCKCURRENT,
            fBlocked        ? MF_CHECKED    : MF_UNCHECKED);

    EnableMenuItem (hmenu,  IDM_ENABLECURRENT,
            fConnected && (fBlocked || fBlockNext) ? MF_ENABLED : MF_GRAYED);
    CheckMenuItem(hmenu,    IDM_ENABLECURRENT,
            !fBlocked       ? MF_CHECKED    : MF_UNCHECKED);

    EnableMenuItem (hmenu,  IDM_ENABLEONECURRENT,
            fConnected && (fBlocked) ? MF_ENABLED : MF_GRAYED);
    CheckMenuItem(hmenu,    IDM_ENABLEONECURRENT,
            fBlockNext      ? MF_CHECKED    : MF_UNCHECKED);

    EnableMenuItem (hmenu,  IDM_BLOCKALLCBS,
            fAny            ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem (hmenu,  IDM_ENABLEALLCBS,
            fAny            ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem (hmenu,  IDM_ENABLEONECB,
            fAny            ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem(hmenu,   IDM_BLOCKNEXTCB,
            fAny || fBlockNextCB ? MF_ENABLED    : MF_GRAYED);
    CheckMenuItem(hmenu,    IDM_BLOCKNEXTCB,
            fBlockNextCB    ? MF_CHECKED    : MF_UNCHECKED);

    EnableMenuItem(hmenu,   IDM_TERMNEXTCB,
            fAny || fTermNextCB ? MF_ENABLED    : MF_GRAYED);
    CheckMenuItem(hmenu,    IDM_TERMNEXTCB,
            fTermNextCB     ? MF_CHECKED    : MF_UNCHECKED);

    // IDM_DELAY - always enabled.

    // IDM_TIMEOUT - alwasy enabled.

    EnableMenuItem (hmenu,  IDM_WINDOWTILE,
            fAny            ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem (hmenu,  IDM_WINDOWCASCADE,
            fAny            ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem (hmenu,  IDM_WINDOWICONS,
            fAny            ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem (hmenu,  IDM_WINDOWCLOSEALL,
            fAny            ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem (hmenu,  IDM_XACTTILE,
            fXactions       ? MF_ENABLED    : MF_GRAYED);

    EnableMenuItem (hmenu,  IDM_XACTCASCADE,
            fXactions       ? MF_ENABLED    : MF_GRAYED);

    CheckMenuItem(hmenu,   IDM_AUTORECONNECT,
            fAutoReconnect  ? MF_CHECKED    : MF_UNCHECKED);

    // IDM_HELPABOUT - always enabled.
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : CloseAllChildren ()                                        *
 *                                                                          *
 *  PURPOSE    : Destroys all MDI child windows.                            *
 *                                                                          *
 ****************************************************************************/
VOID NEAR PASCAL CloseAllChildren ()
{
    HWND hwndT;

    /* hide the MDI client window to avoid multiple repaints */
    ShowWindow(hwndMDIClient,SW_HIDE);

    /* As long as the MDI client has a child, destroy it */
    while ( hwndT = GetWindow (hwndMDIClient, GW_CHILD)){

        /* Skip the icon title windows */
        while (hwndT && GetWindow (hwndT, GW_OWNER))
            hwndT = GetWindow (hwndT, GW_HWNDNEXT);

        if (!hwndT)
            break;

        SendMessage(hwndMDIClient, WM_MDIDESTROY, (DWORD)hwndT, 0L);
    }

    ShowWindow( hwndMDIClient, SW_SHOW);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : CommandHandler ()                                          *
 *                                                                          *
 *  PURPOSE    : Processes all "frame" WM_COMMAND messages.                 *
 *                                                                          *
 ****************************************************************************/
VOID NEAR PASCAL CommandHandler (
HWND hwnd,
DWORD id)

{
    MYCONVINFO *pmci = NULL;

    if (hwndActive)
        pmci = (MYCONVINFO *)GetWindowLong(hwndActive, 0);

    switch (id){
        case IDM_EDITPASTE:
            {
                HANDLE hClipData;
                LPTSTR psz;
                XACT xact;

                if (OpenClipboard(hwnd)) {
                    if (hClipData = GetClipboardData(fmtLink)) {
                        if (psz = GlobalLock(hClipData)) {
                            /*
                             * Create a conversation with the link app and
                             * begin a request and advise start transaction.
                             */
                            xact.hConv = CreateConv(DdeCreateStringHandle(idInst, psz, 0),
                                    DdeCreateStringHandle(idInst, &psz[_tcslen(psz) + 1], 0),
                                    FALSE);
                            if (xact.hConv) {
                                psz += _tcslen(psz) + 1;
                                psz += _tcslen(psz) + 1;
                                xact.ulTimeout = DefTimeout;
                                xact.wType = XTYP_ADVSTART;
                                xact.hDdeData = 0;
#ifdef UNICODE
                                xact.wFmt = CF_UNICODETEXT;
#else
                                xact.wFmt = CF_TEXT;
#endif
                                xact.hszItem = DdeCreateStringHandle(idInst, psz, 0);
                                xact.fsOptions = 0;
                                ProcessTransaction(&xact);
                                xact.wType = XTYP_REQUEST;
                                ProcessTransaction(&xact);
                            }
                            GlobalUnlock(hClipData);
                        }
                    }
                    CloseClipboard();
                }
            }
            break;

        case IDM_CONNECT:
        case IDM_RECONNECT:
            DoDialog(MAKEINTRESOURCE(IDD_CONNECT), (DLGPROC)ConnectDlgProc,
                    id == IDM_RECONNECT, FALSE);
            break;

        case IDM_DISCONNECT:
            if (hwndActive) {
                SendMessage(hwndMDIClient, WM_MDIDESTROY, (DWORD)hwndActive, 0L);
            }
            break;

        case IDM_TRANSACT:
            if (DoDialog(MAKEINTRESOURCE(IDD_TRANSACT), (DLGPROC)TransactDlgProc,
                    (DWORD)(LPTSTR)pmci->hConv, FALSE))
                SetFocus(GetWindow(hwndActive, GW_CHILD));
            break;

        case IDM_ABANDON:
            if (pmci != NULL && IsWindow(pmci->hwndXaction)) {
                DestroyWindow(pmci->hwndXaction);
            }
            break;

        case IDM_ABANDONALL:
            DdeAbandonTransaction(idInst, pmci->hConv, 0L);
            {
                HWND hwndXaction;

                hwndXaction = GetWindow(hwndActive, GW_CHILD);
                while (hwndXaction) {
                    DestroyWindow(hwndXaction);
                    hwndXaction = GetWindow(hwndActive, GW_CHILD);
                }
            }
            break;

        case IDM_BLOCKCURRENT:
            DdeEnableCallback(idInst, pmci->hConv, EC_DISABLE);
            InvalidateRect(hwndActive, NULL, TRUE);
            break;

        case IDM_ENABLECURRENT:
            DdeEnableCallback(idInst, pmci->hConv, EC_ENABLEALL);
            InvalidateRect(hwndActive, NULL, TRUE);
            break;

        case IDM_ENABLEONECURRENT:
            DdeEnableCallback(idInst, pmci->hConv, EC_ENABLEONE);
            InvalidateRect(hwndActive, NULL, TRUE);
            break;

        case IDM_BLOCKALLCBS:
            DdeEnableCallback(idInst, 0, EC_DISABLE);
            InvalidateRect(hwndMDIClient, NULL, TRUE);
            break;

        case IDM_ENABLEALLCBS:
            DdeEnableCallback(idInst, 0, EC_ENABLEALL);
            InvalidateRect(hwndMDIClient, NULL, TRUE);
            break;

        case IDM_ENABLEONECB:
            DdeEnableCallback(idInst, 0, EC_ENABLEONE);
            InvalidateRect(hwndMDIClient, NULL, TRUE);
            break;

        case IDM_BLOCKNEXTCB:
            fBlockNextCB = !fBlockNextCB;
            break;

        case IDM_TERMNEXTCB:
            fTermNextCB = !fTermNextCB;
            break;

        case IDM_DELAY:
            DoDialog(MAKEINTRESOURCE(IDD_VALUEENTRY), (DLGPROC)DelayDlgProc, 0,
                    TRUE);
            break;

        case IDM_TIMEOUT:
            DoDialog(MAKEINTRESOURCE(IDD_VALUEENTRY), (DLGPROC)TimeoutDlgProc, 0,
                    TRUE);
            break;

        case IDM_CONTEXT:
            DoDialog(MAKEINTRESOURCE(IDD_CONTEXT), (DLGPROC)ContextDlgProc, 0, TRUE);
            break;

        case IDM_AUTORECONNECT:
            fAutoReconnect = !fAutoReconnect;
            break;

        /* The following are window commands - these are handled by the
         * MDI Client.
         */
        case IDM_WINDOWTILE:
            /* Tile MDI windows */
            SendMessage (hwndMDIClient, WM_MDITILE, 0, 0L);
            break;

        case IDM_WINDOWCASCADE:
            /* Cascade MDI windows */
            SendMessage (hwndMDIClient, WM_MDICASCADE, 0, 0L);
            break;

        case IDM_WINDOWICONS:
            /* Auto - arrange MDI icons */
            SendMessage (hwndMDIClient, WM_MDIICONARRANGE, 0, 0L);
            break;

        case IDM_WINDOWCLOSEALL:
            CloseAllChildren();
            break;

        case IDM_XACTTILE:
            TileChildWindows(hwndActive);
            break;

        case IDM_XACTCASCADE:
            MyCascadeChildWindows(hwndActive);
            break;

        case IDM_HELPABOUT:{
            DoDialog(MAKEINTRESOURCE(IDD_ABOUT), (DLGPROC)AboutDlgProc, 0, TRUE);
            break;
        }

        default:
           /*
            * This is essential, since there are frame WM_COMMANDS generated
            * by the MDI system for activating child windows via the
            * window menu.
            */
            DefFrameProc(hwnd, hwndMDIClient, WM_COMMAND,
                    (WPARAM)MAKELONG(id, 0), (LONG)(0));
    }
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : MPError (flags, id, ...)                            *
 *                                                                          *
 *  PURPOSE    : Flashes a Message Box to the user. The format string is    *
 *               taken from the STRINGTABLE.                                *
 *                                                                          *
 *  RETURNS    : Returns value returned by MessageBox() to the caller.      *
 *                                                                          *
 ****************************************************************************/
INT FAR cdecl MPError(
DWORD bFlags,
DWORD id,
...)
{
    TCHAR sz[160];
    TCHAR szFmt[128];
    va_list args;
    va_start(args, id);

    LoadString (hInst, id, szFmt, sizeof(szFmt));
    wvsprintf (sz, szFmt, args);
    LoadString (hInst, IDS_APPNAME, szFmt, sizeof(szFmt));
    return MessageBox (hwndFrame, sz, szFmt, bFlags);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : CreateConv()                                               *
 *                                                                          *

*  PURPOSE    :                                                            *
 *                                                                          *
 *  RETURNS    :                                                            *
 *                                                                          *
 ****************************************************************************/
HCONV CreateConv(
HSZ hszApp,
HSZ hszTopic,
BOOL fList)
{
    HCONV hConv;
    HWND hwndConv = 0;
    CONVINFO ci;

    if (fList) {
        hConv = (HCONV)DdeConnectList(idInst, hszApp, hszTopic, 0, &CCFilter);
    } else {
        hConv = DdeConnect(idInst, hszApp, hszTopic, &CCFilter);
    }
    if (hConv) {
        if (fList) {
            ci.hszSvcPartner = hszApp;
            ci.hszTopic = hszTopic;
        } else {
            ci.cb = sizeof(CONVINFO);
            DdeQueryConvInfo(hConv, QID_SYNC, &ci);
        }
        hwndConv = AddConv(ci.hszSvcPartner, ci.hszTopic, hConv, fList);
        // HSZs get freed when window dies.
    }
    if (!hwndConv) {
        DdeFreeStringHandle(idInst, hszApp);
        DdeFreeStringHandle(idInst, hszTopic);
    }
    return(hConv);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : AddConv()                                                  *
 *                                                                          *
 *  PURPOSE    : Creates an MDI window representing a conversation          *
 *               (fList = FALSE) or a set of MID windows for the list of    *
 *               conversations (fList = TRUE).                              *
 *                                                                          *
 *  EFFECTS    : Sets the hUser for the conversation to the created MDI     *
 *               child hwnd.  Keeps the hszs if successful.                 *
 *                                                                          *
 *  RETURNS    : created MDI window handle.                                 *
 *                                                                          *
 ****************************************************************************/
HWND  APIENTRY AddConv(
HSZ hszApp,
HSZ hszTopic,
HCONV hConv,
BOOL fList)
{
    HWND hwnd;
    MDICREATESTRUCT mcs;
    MYCONVINFO mci;

    if (fList) {
        /*
         * Create all child windows FIRST so we have info for list window.
         */
        CONVINFO ci;
        HCONV hConvChild = 0;

        ci.cb = sizeof(CONVINFO);
        while (hConvChild = DdeQueryNextServer((HCONVLIST)hConv, hConvChild)) {
            if (DdeQueryConvInfo(hConvChild, QID_SYNC, &ci)) {
                AddConv(ci.hszSvcPartner, ci.hszTopic, hConvChild, FALSE);
            }
        }
    }

    mcs.szTitle = GetConvTitleText(hConv, hszApp, hszTopic, fList);

    mcs.szClass = fList ? szList : szChild;
    mcs.hOwner  = hInst;
    mcs.x = mcs.cx = CW_USEDEFAULT;
    mcs.y = mcs.cy = CW_USEDEFAULT;
    mcs.style = GetWindow(hwndMDIClient, GW_CHILD) ?
            WS_CLIPCHILDREN : (WS_MAXIMIZE | WS_CLIPCHILDREN);

    // mci.hwndXaction =
    mci.fList = fList;
    mci.hConv = hConv;
    mci.hszTopic = hszTopic;
    mci.hszApp = hszApp;
    // mci.x =
    // mci.y =
    // mci.ci =
    mcs.lParam = (DWORD)(LPTSTR)&mci;

    hwnd = (HWND)SendMessage (hwndMDIClient, WM_MDICREATE, 0,
             (LONG)(LPMDICREATESTRUCT)&mcs);

    MyFree((PTSTR)(DWORD)mcs.szTitle);

    return hwnd;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : GetConvListText()                                          *
 *                                                                          *
 *  RETURN     : Returns a ponter to a string containing a list of          *
 *               conversations contained in the given hConvList freeable    *
 *               by MyFree();                                               *
 *                                                                          *
 ****************************************************************************/
PTSTR GetConvListText(
HCONVLIST hConvList)
{
    HCONV hConv = 0;
    DWORD cConv = 0;
    CONVINFO ci;
    DWORD cb = 0;
    DWORD d;
    TCHAR *psz, *pszStart;

    ci.cb = sizeof(CONVINFO);

    // find out size needed.

    while (hConv = DdeQueryNextServer(hConvList, hConv)) {
        if (DdeQueryConvInfo(hConv, QID_SYNC, &ci)) {
            if (!IsWindow((HWND)ci.hUser)) {
                if (ci.wStatus & ST_CONNECTED) {
                    /*
                     * This conversation doesn't have a corresponding
                     * MDI window.  This is probably due to a reconnection.
                     */
                    ci.hUser = (DWORD)AddConv(ci.hszSvcPartner, ci.hszTopic, hConv, FALSE);
                } else {
                    continue;   // skip this guy - he was closed locally.
                }
            }
            cb += GetWindowTextLength((HWND)ci.hUser);
            if (cConv++)
                cb += 2;        // room for CRLF
        }
    }
    cb++;                       // for terminator.

    // allocate and fill

    if (pszStart = psz = MyAlloc(cb * sizeof(TCHAR))) {
        *psz = TEXT('\0');
        while (hConv = DdeQueryNextServer(hConvList, hConv)) {
            if (DdeQueryConvInfo(hConv, QID_SYNC, &ci) &&
                    IsWindow((HWND)ci.hUser)) {
                d = GetWindowText((HWND)ci.hUser, psz, cb);
                psz += d;
                cb -= d;
                if (--cConv) {
                    *psz++ = TEXT('\r');
                    *psz++ = TEXT('\n');
                    cb -= 2;
                }
            }
        }
    }
    return(pszStart);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : GetConvInfoText()                                          *
 *                                                                          *
 *  PURPOSE    : Returns a pointer to a string that reflects a              *
 *               conversation's information.  Freeable by MyFree();         *
 *                                                                          *
 ****************************************************************************/
PTSTR GetConvInfoText(
HCONV hConv,
CONVINFO *pci)
{
    PTSTR psz;
    PTSTR szApp;

    psz = MyAlloc(300 * sizeof(TCHAR));
    pci->cb = sizeof(CONVINFO);
    if (hConv) {
        if (!DdeQueryConvInfo(hConv, QID_SYNC, (PCONVINFO)pci)) {
            _tcscpy(psz, TEXT("State=Disconnected"));
            return(psz);
        }
        szApp = GetHSZName(pci->hszServiceReq);
        wsprintf(psz,
                TEXT("hUser=0x%lx\r\nhConvPartner=0x%lx\r\nhszServiceReq=%s\r\nStatus=%s\r\nState=%s\r\nLastError=%s"),
                pci->hUser, pci->hConvPartner, (LPTSTR)szApp,
                (LPTSTR)Status2String(pci->wStatus),
                (LPTSTR)State2String(pci->wConvst),
                (LPTSTR)Error2String(pci->wLastError));
        MyFree(szApp);
    } else {
        _tcscpy(psz, Error2String(DdeGetLastError(idInst)));
    }
    return(psz);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : GetConvTitleText()                                         *
 *                                                                          *
 *  PURPOSE    : Creates standard window title text based on parameters.    *
 *                                                                          *
 *  RETURNS    : psz freeable by MyFree()                                   *
 *                                                                          *
 ****************************************************************************/
PTSTR GetConvTitleText(
HCONV hConv,
HSZ hszApp,
HSZ hszTopic,
BOOL fList)
{
    DWORD cb;
    PTSTR psz;

    cb = (DWORD)DdeQueryString(idInst, hszApp, NULL, 0, 0) +
            (DWORD)DdeQueryString(idInst, hszTopic, (LPTSTR)NULL, 0, 0) +
            (fList ? 30 : 20);

    if (psz = MyAlloc(cb * sizeof(TCHAR))) {
        DdeQueryString(idInst, hszApp, psz, cb, 0);
        _tcscat(psz, TEXT("|"));
        DdeQueryString(idInst, hszTopic, &psz[_tcslen(psz)], cb, 0);
        if (fList)
            _tcscat(psz, TEXT(" - LIST"));
        wsprintf(&psz[_tcslen(psz)], TEXT(" - (%lx)"), hConv);
    }
    return(psz);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : Status2String()                                            *
 *                                                                          *
 *  PURPOSE    : Converts a conversation status word to a string and        *
 *               returns a pointer to that string.  The string is valid     *
 *               till the next call to this function.                       *
 *                                                                          *
 ****************************************************************************/
PTSTR Status2String(
DWORD status)
{
    DWORD c, i;
    static TCHAR szStatus[6 * 18];
    static struct {
        TCHAR *szStatus;
        DWORD status;
    } s2s[] = {
        { TEXT("Connected")    ,   ST_CONNECTED },
        { TEXT("Advise")       ,   ST_ADVISE },
        { TEXT("IsLocal")      ,   ST_ISLOCAL },
        { TEXT("Blocked")      ,   ST_BLOCKED },
        { TEXT("Client")       ,   ST_CLIENT },
        { TEXT("Disconnected") ,   ST_TERMINATED },
        { TEXT("BlockNext")    ,   ST_BLOCKNEXT },
    };
#define CFLAGS 7
    szStatus[0] = TEXT('\0');
    c = 0;
    for (i = 0; i < CFLAGS; i++) {
        if (status & s2s[i].status) {
            if (c++)
                _tcscat(szStatus, TEXT(" | "));
            _tcscat(szStatus, s2s[i].szStatus);
        }
    }
    return szStatus;
#undef CFLAGS
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : State2String()                                             *
 *                                                                          *
 *  PURPOSE    : converts a conversation state word to a string and         *
 *               returns a pointer to that string.  The string is valid     *
 *               till the next call to this routine.                        *
 *                                                                          *
 ****************************************************************************/
PTSTR State2String(
DWORD state)
{
    static TCHAR *s2s[] = {
        TEXT("NULL")             ,
        TEXT("Incomplete")       ,
        TEXT("Standby")          ,
        TEXT("Initiating")       ,
        TEXT("ReqSent")          ,
        TEXT("DataRcvd")         ,
        TEXT("PokeSent")         ,
        TEXT("PokeAckRcvd")      ,
        TEXT("ExecSent")         ,
        TEXT("ExecAckRcvd")      ,
        TEXT("AdvSent")          ,
        TEXT("UnadvSent")        ,
        TEXT("AdvAckRcvd")       ,
        TEXT("UnadvAckRcvd")     ,
        TEXT("AdvDataSent")      ,
        TEXT("AdvDataAckRcvd")   ,
        TEXT("?")                ,    // 16
    };

    if (state >= 17)
        return s2s[17];
    else
        return s2s[state];
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : Error2String()                                             *
 *                                                                          *
 *  PURPOSE    : Converts an error code to a string and returns a pointer   *
 *               to that string.  The string is valid until the next call   *
 *               to this function.                                          *
 *                                                                          *
 ****************************************************************************/
PTSTR Error2String(
DWORD error)
{
    static TCHAR szErr[23];
    static TCHAR *e2s[] = {
        TEXT("Advacktimeout")              ,
        TEXT("Busy")                       ,
        TEXT("Dataacktimeout")             ,
        TEXT("Dll_not_initialized")        ,
        TEXT("Dll_usage")                  ,
        TEXT("Execacktimeout")             ,
        TEXT("Invalidparameter")           ,
        TEXT("Low Memory warning")         ,
        TEXT("Memory_error")               ,
        TEXT("Notprocessed")               ,
        TEXT("No_conv_established")        ,
        TEXT("Pokeacktimeout")             ,
        TEXT("Postmsg_failed")             ,
        TEXT("Reentrancy")                 ,
        TEXT("Server_died")                ,
        TEXT("Sys_error")                  ,
        TEXT("Unadvacktimeout")            ,
        TEXT("Unfound_queue_id")           ,
    };
    if (!error) {
        _tcscpy(szErr, TEXT("0"));
    } else if (error > DMLERR_LAST || error < DMLERR_FIRST) {
        _tcscpy(szErr, TEXT("???"));
    } else {
        _tcscpy(szErr, e2s[error - DMLERR_FIRST]);
    }
    return(szErr);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : Type2String()                                              *
 *                                                                          *
 *  PURPOSE    : Converts a wType word and fsOption flags to a string and   *
 *               returns a pointer to that string.  the string is valid     *
 *               until the next call to this function.                      *
 *                                                                          *
 ****************************************************************************/
PTSTR Type2String(
DWORD wType,
DWORD fsOptions)
{
    static TCHAR sz[30];
    static TCHAR o2s[] = TEXT("^!#$X*<?");
    static TCHAR *t2s[] = {
        TEXT("")                 ,
        TEXT("AdvData")          ,
        TEXT("AdvReq")           ,
        TEXT("AdvStart")         ,
        TEXT("AdvStop")          ,
        TEXT("Execute")          ,
        TEXT("Connect")          ,
        TEXT("ConnectConfirm")   ,
        TEXT("XactComplete")    ,
        TEXT("Poke")             ,
        TEXT("Register")         ,
        TEXT("Request")          ,
        TEXT("Term")             ,
        TEXT("Unregister")       ,
        TEXT("WildConnect")      ,
        TEXT("")                 ,
    };
    DWORD bit, c, i;

    _tcscpy(sz, t2s[((wType & XTYP_MASK) >> XTYP_SHIFT)]);
    c = _tcslen(sz);
    sz[c++] = TEXT(' ');
    for (i = 0, bit = 1; i < 7; bit = bit << 1, i++) {
        if (fsOptions & bit)
            sz[c++] = o2s[i];
    }
    sz[c] = TEXT('\0');
    return(sz);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : GetHSZName()                                               *
 *                                                                          *
 *  PURPOSE    : Allocates local memory for and retrieves the string form   *
 *               of an HSZ.  Returns a pointer to the local memory or NULL  *
 *               if failure.  The string must be freed via MyFree().        *
 *                                                                          *
 ****************************************************************************/
PTSTR GetHSZName(
HSZ hsz)
{
    PTSTR psz;
    DWORD cb;

    cb = (DWORD)DdeQueryString(idInst, hsz, NULL, 0, 0) + 1;
    psz = MyAlloc(cb * sizeof(TCHAR));
    DdeQueryString(idInst, hsz, psz, cb, 0);
    return(psz);
}

/****************************************************************************
 *
 *  FUNCTION   : MyMsgFilterProc
 *
 *  PURPOSE    : This filter proc gets called for each message we handle.
 *               This allows our application to properly dispatch messages
 *               that we might not otherwise see because of DDEMLs modal
 *               loop that is used while processing synchronous transactions.
 *
 *               Generally, applications that only do synchronous transactions
 *               in response to user input (as this app does) does not need
 *               to install such a filter proc because it would be very rare
 *               that a user could command the app fast enough to cause
 *               problems.  However, this is included as an example.
 *
 ****************************************************************************/
LRESULT CALLBACK MyMsgFilterProc(
int nCode,
WPARAM wParam,
LPARAM lParam)
{
    UNREFERENCED_PARAMETER(wParam);

#define lpmsg ((LPMSG)lParam)
    if (nCode == MSGF_DDEMGR) {

        /* If a keyboard message is for the MDI , let the MDI client
         * take care of it.  Otherwise, check to see if it's a normal
         * accelerator key.  Otherwise, just handle the message as usual.
         */

        if ( !TranslateMDISysAccel (hwndMDIClient, lpmsg) &&
             !TranslateAccelerator (hwndFrame, hAccel, lpmsg)){
            TranslateMessage (lpmsg);
            DispatchMessage (lpmsg);
        }
        return(1);
    }
    if (nCode < 0) {
        CallNextHookEx(ghhk, nCode, wParam, lParam);
    }
    return(0);
#undef lpmsg
}


DIALOG.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  MODULE      : dialog.c                                                 *
 *                                                                         *
 *  PURPOSE     : Contains all dialog procedures and related functions.    *
 *                                                                         *
 ***************************************************************************/
#include "client.h"
#include "infoctrl.h"
#include <string.h>
#include <stdio.h>
#include "huge.h"

#define MAX_NAME 100    // max size for edit controls with app/topic/item names.
TCHAR szWild[] = TEXT("*");    // used to indicate wild names ("" is also cool)
TCHAR szT[MAX_NAME];     // temp buf for munging names.

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : DoDialog()                                                 *
 *                                                                          *
 *  PURPOSE    : Generic dialog invocation routine.  Handles procInstance   *
 *               stuff, focus management and param passing.                 *
 *  RETURNS    : result of dialog procedure.                                *
 *                                                                          *
 ****************************************************************************/
INT FAR DoDialog(
LPTSTR lpTemplateName,
DLGPROC lpDlgProc,
LONG param,
BOOL fRememberFocus)
{
    INT iRet;
    HWND hwndFocus;

    if (fRememberFocus)
        hwndFocus = GetFocus();
    lpDlgProc = MakeProcInstance(lpDlgProc, hInst);
    iRet = DialogBoxParam(hInst, lpTemplateName, hwndFrame, lpDlgProc, param);
    FreeProcInstance(lpDlgProc);
    if (fRememberFocus)
        SetFocus(hwndFocus);
    return iRet;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   :                                                            *
 *                                                                          *
 *  PURPOSE    :                                                            *
 *                                                                          *
 *  RETURNS    :                                                            *
 *                                                                          *
 ****************************************************************************/
BOOL  APIENTRY AboutDlgProc ( hwnd, msg, wParam, lParam )
HWND          hwnd;
UINT msg;
WPARAM wParam;
LPARAM lParam;
{
    switch (msg){
        case WM_INITDIALOG:
            /* nothing to initialize */
            break;

        case WM_COMMAND:
            switch (LOWORD(wParam)) {
                case IDOK:
                case IDCANCEL:
                    EndDialog(hwnd, 0);
                    break;

                default:
                    return FALSE;
            }
            break;

        default:
            return(FALSE);
    }

    return TRUE;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   :                                                            *
 *                                                                          *
 *  PURPOSE    :                                                            *
 *                                                                          *
 *  RETURNS    :                                                            *
 *                                                                          *
 ****************************************************************************/
BOOL  APIENTRY ConnectDlgProc(
HWND          hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
    static BOOL fReconnect;
    TCHAR szT[MAX_NAME];
    HSZ hszApp, hszTopic;
    MYCONVINFO *pmci;

    switch (msg){
    case WM_INITDIALOG:
        SendDlgItemMessage(hwnd, IDEF_APPLICATION, EM_LIMITTEXT, MAX_NAME, 0);
        SendDlgItemMessage(hwnd, IDEF_TOPIC, EM_LIMITTEXT, MAX_NAME, 0);
        fReconnect = (BOOL)lParam;
        if (fReconnect) {
            PTSTR psz;

            pmci = (MYCONVINFO *)GetWindowLong(hwndActive, 0);
            SetWindowText(hwnd, TEXT("DDE Reconnect List"));
            psz = GetHSZName(pmci->hszApp);
            SetDlgItemText(hwnd, IDEF_APPLICATION, psz);
            MyFree(psz);
            psz = GetHSZName(pmci->hszTopic);
            SetDlgItemText(hwnd, IDEF_TOPIC, psz);
            MyFree(psz);
            ShowWindow(GetDlgItem(hwnd, IDCH_CONNECTLIST), SW_HIDE);
        }
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
            GetDlgItemText(hwnd, IDEF_APPLICATION, szT, MAX_NAME);
            if (!_tcscmp(szT, szWild))
                szT[0] = TEXT('\0');
            hszApp = DdeCreateStringHandle(idInst, szT, 0);

            GetDlgItemText(hwnd, IDEF_TOPIC, szT, MAX_NAME);
            if (!_tcscmp(szT, szWild))
                szT[0] = TEXT('\0');
            hszTopic = DdeCreateStringHandle(idInst, szT, 0);

            if (fReconnect) {
                HCONV hConv;
                CONVINFO ci;
                DWORD cHwnd;
                HWND *aHwnd, *pHwnd, hwndSave;

                ci.cb = sizeof(CONVINFO);
                pmci = (MYCONVINFO *)GetWindowLong(hwndActive, 0);
                hwndSave = hwndActive;

                // count the existing conversations and allocate aHwnd

                cHwnd = 0;
                hConv = 0;
                while (hConv = DdeQueryNextServer((HCONVLIST)pmci->hConv, hConv))
                    cHwnd++;
                aHwnd = (HWND *)MyAlloc(cHwnd * sizeof(HWND));

                // save all the old conversation windows into aHwnd.

                pHwnd = aHwnd;
                hConv = 0;
                while (hConv = DdeQueryNextServer((HCONVLIST)pmci->hConv, hConv)) {
                    DdeQueryConvInfo(hConv, QID_SYNC, &ci);
                    *pHwnd++ = (HWND)ci.hUser;
                }

                // reconnect

                if (!(hConv = (HCONV)DdeConnectList(idInst, hszApp, hszTopic,
                        (HCONVLIST)pmci->hConv, &CCFilter))) {
                    MPError(MB_OK, IDS_DDEMLERR,
                            (LPTSTR)Error2String(DdeGetLastError(idInst)));
                    DdeFreeStringHandle(idInst, hszApp);
                    DdeFreeStringHandle(idInst, hszTopic);
                    return 0;
                }

                // fixup windows corresponding to the new conversations.

                pmci->hConv = hConv;
                hConv = 0;
                while (hConv = DdeQueryNextServer((HCONVLIST)pmci->hConv, hConv)) {
                    DdeQueryConvInfo(hConv, QID_SYNC, &ci);
                    // preserve corresponding window by setting its list
                    // entry to 0
                    for (pHwnd = aHwnd; pHwnd < &aHwnd[cHwnd]; pHwnd++) {
                        if (*pHwnd == (HWND)ci.hUser) {
                            *pHwnd = NULL;
                            break;
                        }
                    }
                }

                // destroy all windows left in the old list

                for (pHwnd = aHwnd; pHwnd < &aHwnd[cHwnd]; pHwnd++)
                    if (*pHwnd) {
                        SendMessage(hwndMDIClient, WM_MDIDESTROY,
                                (WPARAM)*pHwnd, 0L);
                    }
                MyFree((PTSTR)aHwnd);

                // create any new windows needed

                hConv = 0;
                while (hConv = DdeQueryNextServer((HCONVLIST)pmci->hConv, hConv)) {
                    DdeQueryConvInfo(hConv, QID_SYNC, &ci);
                    if (ci.hUser) {
                        InvalidateRect((HWND)ci.hUser, NULL, TRUE);
                    } else {
                        AddConv(ci.hszSvcPartner, ci.hszTopic, hConv, FALSE);
                    }
                }

                // make list window update itself

                InvalidateRect(hwndSave, NULL, TRUE);
                SetFocus(hwndSave);
            } else {
                if (!CreateConv(hszApp, hszTopic,
                        IsDlgButtonChecked(hwnd, IDCH_CONNECTLIST))) {
                    MPError(MB_OK, IDS_DDEMLERR, (LPTSTR)Error2String(DdeGetLastError(idInst)));
                    return 0;
                }
            }
            DdeFreeStringHandle(idInst, hszApp);
            DdeFreeStringHandle(idInst, hszTopic);
            // fall through
        case IDCANCEL:
            EndDialog(hwnd, 0);
            break;

        default:
            return(FALSE);
        }
        break;

    default:
        return(FALSE);
    }
}

/*
 * Fills a XACT structure and calls ProcessTransaction.
 *
 * On initiation lParam == hConv.
 */
/****************************************************************************
 *                                                                          *
 *  FUNCTION   :                                                            *
 *                                                                          *
 *  PURPOSE    :                                                            *
 *                                                                          *
 *  RETURNS    :                                                            *
 *                                                                          *
 ****************************************************************************/
BOOL  APIENTRY TransactDlgProc(
HWND          hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
    static DWORD id2type[] = {
        XTYP_REQUEST,       // IDCH_REQUEST
        XTYP_ADVSTART,      // IDCH_ADVISE
        XTYP_ADVSTOP,       // IDCH_UNADVISE
        XTYP_POKE,          // IDCH_POKE
        XTYP_EXECUTE,       // IDCH_EXECUTE
    };
    static XACT *pxact;     // ONLY ONE AT A TIME!
    INT i;

    switch (msg){
    case WM_INITDIALOG:
        pxact = (XACT *)MyAlloc(sizeof(XACT));
        pxact->hConv = (HCONV)lParam;
        pxact->fsOptions = DefOptions;
        pxact->ulTimeout = DefTimeout;

        // The item index == the index to the format atoms in aFormats[].
        for (i = 0; i < CFORMATS; i++)
            SendDlgItemMessage(hwnd, IDCB_FORMAT, CB_INSERTSTRING, i,
                    (DWORD)(LPTSTR)aFormats[i].sz);
        SendDlgItemMessage(hwnd, IDCB_FORMAT, CB_INSERTSTRING, i,
                (DWORD)(LPTSTR)TEXT("NULL"));
        SendDlgItemMessage(hwnd, IDCB_FORMAT, CB_SETCURSEL, 0, 0);
        CheckRadioButton(hwnd, IDCH_REQUEST, IDCH_EXECUTE, IDCH_REQUEST);
        SendDlgItemMessage(hwnd, IDEF_ITEM, EM_LIMITTEXT, MAX_NAME, 0);

        // If there is a top transaction window, use its contents to
        // anticipate what the user will want to do.

        if (IsWindow(hwndActive)) {
            HWND hwndXaction;
            XACT *pxact;
            PTSTR pszItem;

            hwndXaction = GetWindow(hwndActive, GW_CHILD);
            if (IsWindow(hwndXaction)) {
                pxact = (XACT *)GetWindowLong(hwndXaction, GWL_USER);
                pszItem = GetHSZName(pxact->hszItem);
                if ((pxact->wType & XTYP_ADVSTART) == XTYP_ADVSTART ||
                        pxact->wType == XTYP_ADVDATA) {
                    CheckRadioButton(hwnd, IDCH_REQUEST, IDCH_EXECUTE, IDCH_UNADVISE);
                }
                SetDlgItemText(hwnd, IDEF_ITEM, pszItem);
                for (i = 0; i < CFORMATS; i++) {
                    if (aFormats[i].fmt == pxact->wFmt) {
                        SendDlgItemMessage(hwnd, IDCB_FORMAT, CB_SETCURSEL, i, 0);
                        break;
                    }
                }
                MyFree(pszItem);
            }
        }
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDCH_EXECUTE:
            SetDlgItemText(hwnd, IDEF_ITEM, TEXT(""));
        case IDCH_REQUEST:
        case IDCH_ADVISE:
        case IDCH_UNADVISE:
        case IDCH_POKE:
            EnableWindow(GetDlgItem(hwnd, IDEF_ITEM), LOWORD(wParam) != IDCH_EXECUTE);
            EnableWindow(GetDlgItem(hwnd, IDTX_ITEM), LOWORD(wParam) != IDCH_EXECUTE);
            break;

        case IDOK:
        case IDBN_OPTIONS:
            {
                INT id;

                // set pxact->wType

                for (id = IDCH_REQUEST; id <= IDCH_EXECUTE; id++) {
                    if (IsDlgButtonChecked(hwnd, id)) {
                        pxact->wType = id2type[id - IDCH_REQUEST];
                        break;
                    }
                }

                if (LOWORD(wParam) == IDBN_OPTIONS) {
                    DoDialog(MAKEINTRESOURCE(IDD_ADVISEOPTS),
                            (DLGPROC)AdvOptsDlgProc, (LONG)pxact, TRUE);
                    return 0;
                }

                id = (INT)SendDlgItemMessage(hwnd, IDCB_FORMAT, CB_GETCURSEL, 0, 0);
                if (id == LB_ERR) {
                    return 0;
                }
                if (id == CFORMATS)
                    pxact->wFmt = 0;
                else
                    pxact->wFmt = aFormats[id].fmt;

                if (pxact->wType == XTYP_ADVSTART) {
                    if (pxact->fsOptions & XOPT_NODATA)
                        pxact->wType |= XTYPF_NODATA;
                    if (pxact->fsOptions & XOPT_ACKREQ)
                        pxact->wType |= XTYPF_ACKREQ;
                }

                GetDlgItemText(hwnd, IDEF_ITEM, szT, MAX_NAME);
                pxact->hszItem = DdeCreateStringHandle(idInst, szT, 0);

                pxact->hDdeData = 0;
                /*
                 * If this transaction needs data, invoke data input dialog.
                 */
                if (pxact->wType == XTYP_POKE || pxact->wType == XTYP_EXECUTE) {
                    if (!DoDialog(MAKEINTRESOURCE(IDD_TEXTENTRY),
                            (DLGPROC)TextEntryDlgProc, (DWORD)(LPTSTR)pxact,
                            TRUE))
                        return 0;
                }

                // now start the transaction

                ProcessTransaction(pxact);
                MyFree((PTSTR)pxact);
            }
            EndDialog(hwnd, 1);
            break;

        case IDCANCEL:
            MyFree((PTSTR)pxact);
            EndDialog(hwnd, 0);
            break;

        default:
            return(FALSE);
        }
        break;

    case WM_DESTROY:
        break;

    default:
        return(FALSE);
    }
    return 0;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : AdvOptsDlgProc                                             *
 *                                                                          *
 *  LIMITATIONS: Because we use Get/SetDlgItemInt() the timeout value used  *
 *               can't be greater than 65K ms.                              *
 *                                                                          *
 *  RETURNS    :                                                            *
 *                                                                          *
 ****************************************************************************/
BOOL  APIENTRY AdvOptsDlgProc(
HWND          hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
    static struct {
        DWORD id;
        DWORD opt;
    } id2Opt[] = {
        {   IDCH_NODATA        ,   XOPT_NODATA             }   ,
        {   IDCH_ACKREQ        ,   XOPT_ACKREQ             }   ,
        {   IDCH_DISABLEFIRST  ,   XOPT_DISABLEFIRST       }   ,
        {   IDCH_ABANDON       ,   XOPT_ABANDONAFTERSTART  }   ,
        {   IDCH_BLOCKRESULT   ,   XOPT_BLOCKRESULT        }   ,
        {   IDCH_ASYNC         ,   XOPT_ASYNC              }   ,
    };
#define CCHBOX  6
    INT i;
    static XACT *pxact; // only one instance at a time!!

    switch (msg){
    case WM_INITDIALOG:
        pxact = (XACT *)lParam;

        for (i = 0; i < CCHBOX; i++) {
            CheckDlgButton(hwnd, id2Opt[i].id, pxact->fsOptions & id2Opt[i].opt);
        }
        SetDlgItemInt(hwnd, IDEF_TIMEOUT, (DWORD)pxact->ulTimeout, FALSE);
        if (pxact->wType != XTYP_ADVSTART) {
            EnableWindow(GetDlgItem(hwnd, IDCH_NODATA), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDCH_ACKREQ), FALSE);
        }
        SendMessage(hwnd, WM_COMMAND, (WPARAM)MAKELONG(IDCH_ASYNC, 0), (LONG)(0));   // enable async checkboxes
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDCH_ASYNC:
            {
                BOOL fEnable;

                fEnable = IsDlgButtonChecked(hwnd, IDCH_ASYNC);
                EnableWindow(GetDlgItem(hwnd, IDCH_DISABLEFIRST), fEnable);
                EnableWindow(GetDlgItem(hwnd, IDCH_ABANDON), fEnable);
                EnableWindow(GetDlgItem(hwnd, IDCH_BLOCKRESULT), fEnable);
                EnableWindow(GetDlgItem(hwnd, IDEF_TIMEOUT), !fEnable);
            }
            break;

        case IDOK:
            pxact->fsOptions = 0;
            for (i = 0; i < CCHBOX; i++) {
                if (IsDlgButtonChecked(hwnd, id2Opt[i].id))
                    pxact->fsOptions |= id2Opt[i].opt;
            }
            if (!(pxact->fsOptions & XOPT_ASYNC))
                pxact->ulTimeout = (DWORD)GetDlgItemInt(hwnd, IDEF_TIMEOUT,
                    (BOOL *)&i, FALSE);
            // fall through
        case IDCANCEL:
            EndDialog(hwnd, 0);
            break;
        }
        break;

    default:
        return(FALSE);
    }
    return 0;
#undef CCHBOX
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : TextEntryDlgProc                                           *
 *                                                                          *
 *  PURPOSE    : Allows user to enter text data which is to be sent to a    *
 *               server.  The user can opt to have a huge text piece of     *
 *               data created automaticlly.                                 *
 *               It uses the XACT structure for passing info in and out.    *
 *               Must have wFmt and hszItem set on entry.                   *
 *               Sets hDDEData on return if TRUE was returned.              *
 *                                                                          *
 *  RETURNS    : TRUE on success, FALSE on failure or cancel                *
 *                                                                          *
 ****************************************************************************/
BOOL  APIENTRY TextEntryDlgProc(
HWND          hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
    static XACT FAR *pxact;
    DWORD cb;
    LONG length;
    LPBYTE pData;
    BOOL fOwned;
    INT id, i;

    switch (msg){
    case WM_INITDIALOG:
        pxact = (XACT FAR *)lParam;
        fOwned = FALSE;
        for (i = 0; i < (INT)cOwned; i++) {
            if (aOwned[i].wFmt == pxact->wFmt &&
                    aOwned[i].hszItem == pxact->hszItem) {
                fOwned = TRUE;
                break;
            }
        }
        EnableWindow(GetDlgItem(hwnd, IDBN_USEOWNED), fOwned);
        CheckDlgButton(hwnd, IDCH_MAKEOWNED, 0);
        EnableWindow(GetDlgItem(hwnd, IDCH_MAKEOWNED), cOwned < MAX_OWNED);
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
        case IDBN_GENHUGE:
            fOwned = IsDlgButtonChecked(hwnd, IDCH_MAKEOWNED);
            cb = (SendDlgItemMessage(hwnd, IDEF_DATA, WM_GETTEXTLENGTH, 0, 0) + 1) * sizeof(TCHAR);
            pxact->hDdeData = DdeCreateDataHandle(idInst, NULL, 0,
                    cb, pxact->hszItem,
                    pxact->wFmt, fOwned ? HDATA_APPOWNED : 0);
            if (pxact->hDdeData == 0) {
                MessageBeep(0);
                return(0);
            }
            //
            // Note that at this time we have not yet given the data handle
            // to DDEML for transmission to any application, therefore, we
            // are at liberty to write to it using DdeAccessData() or any
            // other DDEML api.  It is only data handles received from DDEML
            // or given to DDEML for transmission that are readonly.
            //
            pData = DdeAccessData(pxact->hDdeData, NULL);
            if (pData == NULL) {
                MessageBeep(0);
                return(0);
            }
            GetDlgItemText(hwnd, IDEF_DATA, (LPTSTR)pData, (DWORD)cb);
            DdeUnaccessData(pxact->hDdeData);
            if (LOWORD(wParam) == IDBN_GENHUGE) {
                TCHAR szT[40];

                /*
                 * we assume in this case that the text entered is the decimal
                 * value of the size of the huge object desired.  We parse
                 * this string and create a randomly generated huge block
                 * of text data and place it into pxact->hDdeData.
                 */
                memcpy(szT, pData, min((DWORD)cb, 40));
                szT[39] = TEXT('\0');
                if (_stscanf(szT, TEXT("%ld"), &length) == 1) {
                    DdeFreeDataHandle(pxact->hDdeData);
                    pxact->hDdeData = CreateHugeDataHandle(length, 4325,
                            345, 5, pxact->hszItem, pxact->wFmt,
                            fOwned ? HDATA_APPOWNED : 0);
                } else {
                    /*
                     * The string cannot be parsed.  Inform the user of
                     * what is expected.
                     */
                    MPError(MB_OK, IDS_BADLENGTH);
                    return 0;
                }
            }
            if (fOwned) {
                aOwned[cOwned].hData = pxact->hDdeData;
                aOwned[cOwned].hszItem = pxact->hszItem;
                aOwned[cOwned].wFmt = pxact->wFmt;
                cOwned++;
            }
            EndDialog(hwnd, TRUE);
            break;

        case IDBN_USEOWNED:
            /*
             * the user has chosen to use an existing owned data for sending
             * to the server.
             */
            id = DoDialog(MAKEINTRESOURCE(IDD_HDATAVIEW), (DLGPROC)ViewHandleDlgProc,
                    (LONG)pxact, TRUE);

            switch (id) {
            case IDCANCEL:
                return(0);

            case IDOK:
                EndDialog(hwnd, TRUE);

            case IDBN_VIEW:
                pData = DdeAccessData(pxact->hDdeData, NULL);
                SetDlgItemText(hwnd, IDEF_DATA, (LPTSTR)pData);
                DdeUnaccessData(pxact->hDdeData);
                break;
            }
            break;

        case IDCANCEL:
            EndDialog(hwnd, FALSE);
            break;
        }
        break;

    default:
        return(FALSE);
    }
}

BOOL  APIENTRY ViewHandleDlgProc(
HWND          hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
    static XACT FAR *pxact;
    INT i, itm;

    switch (msg){
    case WM_INITDIALOG:
        pxact = (XACT FAR *)lParam;
        // load listbox with handles that fit pxact constraints

        for (i = 0; i < (INT)cOwned; i++) {
            if (aOwned[i].hszItem == pxact->hszItem &&
                    aOwned[i].wFmt == pxact->wFmt) {
                wsprintf(szT, TEXT("[%d] %lx : length=%ld"), i, aOwned[i].hData,
                        DdeGetData(aOwned[i].hData, NULL, 0, 0));
                SendDlgItemMessage(hwnd, IDLB_HANDLES, LB_ADDSTRING, 0, (LONG)(LPTSTR)szT);
            }
        }
        SendDlgItemMessage(hwnd, IDLB_HANDLES, LB_SETCURSEL, 0, 0);
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:          // use selectted handle
        case IDBN_DELETE:   // delete selected handle
        case IDBN_VIEW:     // view selected handle
            itm = (INT)SendDlgItemMessage(hwnd, IDLB_HANDLES, LB_GETCURSEL, 0, 0);
            if (itm != LB_ERR) {
                SendDlgItemMessage(hwnd, IDLB_HANDLES, LB_GETTEXT, itm, (LONG)(LPTSTR)szT);
                _stscanf(szT, TEXT("[%d]"), &i);
                pxact->hDdeData = aOwned[i].hData;
                switch (LOWORD(wParam)) {
                case IDOK:          // use selectted handle
                    EndDialog(hwnd, LOWORD(wParam));
                    break;

                case IDBN_DELETE:   // delete selected handle
                    DdeFreeDataHandle(aOwned[i].hData);
                    aOwned[i] = aOwned[--cOwned];
                    SendDlgItemMessage(hwnd, IDLB_HANDLES, LB_DELETESTRING, itm, 0);
                    if (SendDlgItemMessage(hwnd, IDLB_HANDLES, LB_GETCOUNT, 0, 0) == 0)
                        EndDialog(hwnd, IDCANCEL);
                    break;

                case IDBN_VIEW:     // view selected handle
                    EndDialog(hwnd, LOWORD(wParam));
                }
            }
            break;

        case IDCANCEL:
            EndDialog(hwnd, FALSE);
            break;
        }
        break;

    default:
        return(FALSE);
    }
}

BOOL  APIENTRY DelayDlgProc(
HWND          hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
    switch (msg){
    case WM_INITDIALOG:
        SetWindowText(hwnd, TEXT("Advise data response time"));
        SetDlgItemInt(hwnd, IDEF_VALUE, wDelay, FALSE);
        SetDlgItemText(hwnd, IDTX_VALUE, TEXT("Delay in milliseconds:"));
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
            wDelay = (DWORD)GetDlgItemInt(hwnd, IDEF_VALUE, NULL, FALSE);
        case IDCANCEL:
            EndDialog(hwnd, 0);
            break;

        default:
            return(FALSE);
        }
        break;

    default:
        return(FALSE);
    }
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : TimeoutDlgProc()                                           *
 *                                                                          *
 *  PURPOSE    : Allows user to alter the synchronous timeout value.        *
 *                                                                          *
 *  RETURNS    : TRUE on success, FALSE on cancel or failure.               *
 *                                                                          *
 ****************************************************************************/
BOOL  APIENTRY TimeoutDlgProc(
HWND          hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
    switch (msg){
    case WM_INITDIALOG:
        SetWindowText(hwnd, TEXT("Synchronous transaction timeout"));
        SetDlgItemInt(hwnd, IDEF_VALUE, (INT)DefTimeout, FALSE);
        SetDlgItemText(hwnd, IDTX_VALUE, TEXT("Timeout in milliseconds:"));
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
            DefTimeout = GetDlgItemInt(hwnd, IDEF_VALUE, NULL, FALSE);
        case IDCANCEL:
            EndDialog(hwnd, 0);
            break;

        default:
            return(FALSE);
        }
        break;

    default:
        return(FALSE);
    }
}

BOOL  APIENTRY ContextDlgProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
    BOOL fSuccess;

    switch (msg){
    case WM_INITDIALOG:
        SetDlgItemInt(hwnd, IDEF_FLAGS, CCFilter.wFlags, FALSE);
        SetDlgItemInt(hwnd, IDEF_COUNTRY, CCFilter.wCountryID, FALSE);
        SetDlgItemInt(hwnd, IDEF_CODEPAGE, CCFilter.iCodePage, TRUE);
        SetDlgItemInt(hwnd, IDEF_LANG, CCFilter.dwLangID, FALSE);

SetDlgItemInt(hwnd, IDEF_SECURITY, CCFilter.dwSecurity, FALSE);
        CheckRadioButton(hwnd, IDRB_IL_ANON, IDRB_IL_DELEGATE,
                IDRB_IL_ANON + (int)CCFilter.qos.ImpersonationLevel);
        return(1);
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
            CCFilter.wFlags = (WORD)GetDlgItemInt(hwnd, IDEF_FLAGS, &fSuccess, FALSE);
            if (!fSuccess) return(0);
            CCFilter.wCountryID = (WORD)GetDlgItemInt(hwnd, IDEF_COUNTRY, &fSuccess, FALSE);
            if (!fSuccess) return(0);
            CCFilter.iCodePage = GetDlgItemInt(hwnd, IDEF_CODEPAGE, &fSuccess, TRUE);
            if (!fSuccess) return(0);
            CCFilter.dwLangID = (DWORD)GetDlgItemInt(hwnd, IDEF_LANG, &fSuccess, FALSE);
            if (!fSuccess) return(0);
            CCFilter.dwSecurity = (DWORD)GetDlgItemInt(hwnd, IDEF_SECURITY, &fSuccess, FALSE);
            if (!fSuccess) return(0);
            if (IsDlgButtonChecked(hwnd, IDRB_IL_ANON)) {
                CCFilter.qos.ImpersonationLevel = SecurityAnonymous;
            } else if (IsDlgButtonChecked(hwnd, IDRB_IL_ID)) {
                CCFilter.qos.ImpersonationLevel = SecurityIdentification;
            } else if (IsDlgButtonChecked(hwnd, IDRB_IL_IMP)) {
                CCFilter.qos.ImpersonationLevel = SecurityImpersonation;
            } else if (IsDlgButtonChecked(hwnd, IDRB_IL_DELEGATE)) {
                CCFilter.qos.ImpersonationLevel = SecurityDelegation;
            }
            // fall through
        case IDCANCEL:
            EndDialog(hwnd, 0);
            break;

        default:
            return(FALSE);
        }
        break;
    }
    return(FALSE);
}

VOID Delay(
DWORD delay)
{
    MSG msg;

    delay = GetCurrentTime() + delay;
    while (GetCurrentTime() < delay) {
        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
}


DIALOG.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define IDEF_DATA           101
#define IDCH_NODATA         102
#define IDCH_ACKREQ         103
#define IDCH_DISABLEFIRST   104
#define IDCH_ABANDON        105
#define IDCH_BLOCKRESULT    106
#define IDCH_ASYNC          107
#define IDEF_TIMEOUT        109
#define IDEF_ITEM           110
#define IDCB_FORMAT         111
#define IDBN_OPTIONS        112
#define IDCH_REQUEST        113
#define IDCH_ADVISE         114
#define IDCH_UNADVISE       115
#define IDCH_POKE           116
#define IDCH_EXECUTE        117
#define IDEF_APPLICATION    118
#define IDEF_TOPIC          119
#define IDCH_CONNECTLIST    120
#define IDEF_VALUE          121
#define IDTX_VALUE          122
#define IDBN_GENHUGE        123
#define IDTX_ITEM           124
#define IDBN_USEOWNED       125
#define IDEF_FLAGS          126
#define IDEF_COUNTRY        127
#define IDEF_CODEPAGE       128
#define IDEF_LANG           129
#define IDEF_SECURITY       130
#define IDD_TEXTENTRY       900
#define IDD_ADVISEOPTS      901
#define IDD_TRANSACT        902
#define IDD_CONNECT         903
#define IDD_VALUEENTRY      904
#define IDD_ABOUT           905
#define IDD_CONTEXT         906
#define IDD_HDATAVIEW       100
#define IDBN_VIEW           131
#define IDCH_MAKEOWNED      132
#define IDBN_DELETE         133
#define IDLB_HANDLES        134
#define IDRB_IL_ANON        908
#define IDRB_IL_ID          909
#define IDRB_IL_IMP         910
#define IDRB_IL_DELEGATE    911


HUGE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  MODULE      : huge.c                                                   *
 *                                                                         *
 *  PURPOSE     : This contains functions useful for generating and        *
 *                verifying huge text data blocks.                         *
 *                                                                         *
 ***************************************************************************/

#include <windows.h>
#include <string.h>
#include <tchar.h>
#include <stdio.h>
#include <ddeml.h>
#include "huge.h"

extern DWORD idInst;
#define BUFSZ   435

LONG lseed, lmult, ladd;
TCHAR szT[BUFSZ];

VOID SetMyRand(LONG seed, LONG mult, LONG add);
TCHAR MyRand(VOID);
BOOL RandTest(LONG length, LONG seed, LONG mult, LONG add);

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : SetMyRand()                                                *
 *                                                                          *
 *  PURPOSE    : Transfers random sequence generation variables to globals. *
 *                                                                          *
 ****************************************************************************/
VOID SetMyRand(
LONG seed,
LONG mult,
LONG add)
{
    lseed = seed;
    lmult = mult;
    ladd = add;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : MyRand()                                                   *
 *                                                                          *
 *  PURPOSE    : Generates the next random character in a sequence.         *
 *                                                                          *
 *  RETURNS    : the character generated                                    *
 *                                                                          *
 ****************************************************************************/
TCHAR MyRand()
{
    TCHAR c;

    lseed = lseed * lmult + ladd;
    c = (TCHAR)(LOWORD(lseed) ^ HIWORD(lseed));
    return((TCHAR)((c & (TCHAR)0x4f) + TEXT(' ')));   // 0x20 - 0x6f - all printable
}

/*
 * This function allocates and fills a HUGE data handle with a verifiable
 * text string.
 *
 * The format of the text string is:
 * "<length>=<seed>*<mult>+<add>;---data of length <length>---\0"
 * all values are stored in base 16 numbers.
 */
/****************************************************************************
 *                                                                          *
 *  FUNCTION   : CreateHugeDataHandle()                                     *
 *                                                                          *
 *  PURPOSE    : Generates a huge pseudo-random sequence of printable       *
 *               characters of the length given and places then into        *
 *               a DDEML data handle.                                       *
 *                                                                          *
 *  RETURNS    : The data handle created or 0 on failure.                   *
 *                                                                          *
 ****************************************************************************/
HDDEDATA CreateHugeDataHandle(
LONG length,
LONG seed,
LONG mult,
LONG add,
HSZ hszItem,
DWORD wFmt,
DWORD afCmd)
{
    register DWORD cb;
    HDDEDATA hData;
    DWORD cbData;
    TCHAR *psz;

    wsprintf(szT, TEXT("%ld=%ld*%ld+%ld;"), length, seed, mult, add);
    cb = (_tcslen(szT) + 1) * sizeof(TCHAR);
    hData = DdeCreateDataHandle(idInst, (PBYTE)szT, cb, 0, hszItem, wFmt, afCmd);
    if (hData)
        hData = DdeAddData(hData, NULL, 0, cb + length);
    cbData = cb;
    SetMyRand(seed, mult, add);
    while (hData && (length > 0)) {
        psz = szT;
        cb = BUFSZ;
        while (cb--)
            *psz++ = MyRand();
        hData = DdeAddData(hData, (PBYTE)szT, min(length, BUFSZ), cbData);
        cbData += BUFSZ;
        length -= BUFSZ;
    }
    return(hData);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : CheckHugeData()                                            *
 *                                                                          *
 *  PURPOSE    : Verifies the correctness of a pseudo-random character      *
 *               sequence generated by CreateHugeData.                      *
 *                                                                          *
 *  RETURNS    : TRUE if verified ok, FALSE otherwise.                      *
 *                                                                          *
 ****************************************************************************/
BOOL CheckHugeData(
HDDEDATA hData)
{
    LONG length;
    LONG seed;
    LONG mult;
    LONG add;
    TCHAR *psz;
    DWORD cbOff;
    DWORD cb;

    if (!DdeGetData(hData, (PBYTE)szT, BUFSZ, 0))
        return(FALSE);
    szT[BUFSZ - 1] = TEXT('\0');
    psz = _tcschr(szT, TEXT(';'));
    if (psz == NULL)
        return(FALSE);
    *psz = TEXT('\0');

    if (_stscanf(szT, TEXT("%ld=%ld*%ld+%ld"), &length, &seed, &mult, &add) != 4)
        return(FALSE);

    if (length < 0)
        return(FALSE);
    SetMyRand(seed, mult, add);
    cbOff = _tcslen(szT) + 1;
    while (length > 0) {
        DdeGetData(hData, (PBYTE)szT, BUFSZ, cbOff);
        psz = szT;
        cb = BUFSZ;
        while (length-- && cb--)
            if (*psz++ != MyRand())
                return(FALSE);
        cbOff += BUFSZ;
        length -= BUFSZ;
    }
    return(TRUE);
}

#if 0
/****************************************************************************
 *                                                                          *
 *  FUNCTION   : RandTest()                                                 *
 *                                                                          *
 *  PURPOSE    : Verifies the correctness of CreateHugeDataHandle() and     *
 *               CheckHugeData().                                           *
 *                                                                          *
 *  RETURNS    :                                                            *
 *                                                                          *
 ****************************************************************************/
BOOL RandTest(
LONG length,
LONG seed,
LONG mult,
LONG add)
{
    HDDEDATA hData;
    BOOL fSuccess;

    hData = CreateHugeDataHandle(length, seed, mult, add, 0, 1, 0);
    if (!hData)
        return(FALSE);
    fSuccess = CheckHugeData(hData);
    DdeFreeDataHandle(hData);
    return(fSuccess);
}
#endif


HUGE.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// header file for HUGE data handle testing module

// PROCS

HDDEDATA CreateHugeDataHandle(LONG length, LONG seed, LONG mult, LONG add,
        HSZ hszItem, DWORD wFmt, DWORD afCmd);
BOOL CheckHugeData(HDDEDATA hData);


INFOCTRL.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  MODULE      : infoctrl.c                                               *
 *                                                                         *
 *  PURPOSE     : Functions for the infoctrl control class                 *
 *                                                                         *
 ***************************************************************************/
/*
 * INFOCTRL.C
 *
 * This module implements a custom information display control which
 * can present up to 7 seperate strings of information at once and is
 * sizeable and moveable with the mouse.
 */

#include <windows.h>
#include <string.h>
#include <tchar.h>
#include <memory.h>
#include "infoctrl.h"
#include "track.h"

TCHAR szClass[] = TEXT("InfoCtrl_class");
DWORD cCreated = 0;
TCHAR szNULL[] = TEXT("");
INT cxMargin = 0;
INT cyMargin = 0;
HBRUSH hFocusBrush;

LONG  APIENTRY InfoCtrlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
VOID MyDrawText(HDC hdc, LPRECT lprc, PTSTR psz, DWORD wFormat);
VOID DrawFocus(HDC hdc, HWND hwnd, DWORD style);
INT CountWindows(HWND hwndParent);
VOID GetCascadeWindowPos(HWND hwndParent, INT  iWindow, LPRECT lprc);

/****************************************************************************
 *                                                                          *
 *  FUNCTION   :                                                            *
 *                                                                          *
 *  PURPOSE    :                                                            *
 *                                                                          *
 *  RETURNS    :                                                            *
 *                                                                          *
 ****************************************************************************/
HWND CreateInfoCtrl(
LPTSTR pszCenter,              // NULL is ok.
INT x,
INT y,
INT cx,
INT cy,
HWND hwndParent,
HANDLE hInst,
LPTSTR pszUL,                // NULLs here are fine.
LPTSTR pszUC,
LPTSTR pszUR,
LPTSTR pszLL,
LPTSTR pszLC,
LPTSTR pszLR,
DWORD  style,
HMENU id,
DWORD dwUser)
{
    INFOCTRL_DATA *picd;
    HWND hwnd;

    if (!cCreated) {
        WNDCLASS wc;
        TEXTMETRIC metrics;
        HDC hdc;

        wc.style = CS_VREDRAW | CS_HREDRAW;
        wc.lpfnWndProc = InfoCtrlWndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = ICCBWNDEXTRA;
        wc.hInstance = hInst;
        wc.hIcon = NULL;
        wc.hCursor = NULL;
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wc.lpszMenuName =  NULL;
        wc.lpszClassName = szClass;

        RegisterClass(&wc);

        hdc = GetDC(hwndParent);
        GetTextMetrics(hdc, &metrics);
        cyMargin = metrics.tmHeight;
        cxMargin = metrics.tmAveCharWidth * 2;
        ReleaseDC(hwndParent, hdc);
        hFocusBrush = CreateSolidBrush(RGB(0, 0, 255));
    }

    if (!(picd = (INFOCTRL_DATA *)LocalAlloc(LPTR, sizeof(INFOCTRL_DATA))))
        return(FALSE);

    if (pszCenter) {
        picd->pszCenter = (PTSTR)(PTSTR)LocalAlloc(LPTR,
                (_tcslen(pszCenter) + 1) * sizeof(TCHAR));
        _tcscpy(picd->pszCenter, pszCenter);
    } else {
        picd->pszCenter = NULL;
    }

    if (pszUL) {
        picd->pszUL = (PTSTR)(PTSTR)LocalAlloc(LPTR,
            (_tcslen(pszUL) + 1) * sizeof(TCHAR));
        _tcscpy(picd->pszUL, pszUL);
    } else {
        picd->pszUL = NULL;
    }
    if (pszUC) {
        picd->pszUC = (PTSTR)LocalAlloc(LPTR,
            (_tcslen(pszUC) + 1) * sizeof(TCHAR));
        _tcscpy(picd->pszUC, pszUC);
    } else {
        picd->pszUC = NULL;
    }
    if (pszUR) {
        picd->pszUR = (PTSTR)LocalAlloc(LPTR,
            (_tcslen(pszUR) + 1) * sizeof(TCHAR));
        _tcscpy(picd->pszUR, pszUR);
    } else {
        picd->pszUR = NULL;
    }
    if (pszLL) {
        picd->pszLL = (PTSTR)LocalAlloc(LPTR,
            (_tcslen(pszLL) + 1) * sizeof(TCHAR));
        _tcscpy(picd->pszLL, pszLL);
    } else {
        picd->pszLL = NULL;
    }
    if (pszLC) {
        picd->pszLC = (PTSTR)LocalAlloc(LPTR,
            (_tcslen(pszLC) + 1) * sizeof(TCHAR));
        _tcscpy(picd->pszLC, pszLC);
    } else {
        picd->pszLC = NULL;
    }
    if (pszLR) {
        picd->pszLR = (PTSTR)LocalAlloc(LPTR,
            (_tcslen(pszLR) + 1) * sizeof(TCHAR));
        _tcscpy(picd->pszLR, pszLR);
    } else {
        picd->pszLR = NULL;
    }

    picd->style = style;
    picd->hInst = hInst;

    if (hwnd = CreateWindow(szClass, szNULL,
            WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
            x, y, cx, cy, hwndParent, id, hInst, (LPTSTR)picd)) {
        cCreated++;
        SetWindowLong(hwnd, GWL_USER, dwUser);
        BringWindowToTop(hwnd);
        ShowWindow(hwnd, SW_SHOW);
        return(hwnd);
    }
    return(FALSE);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : MyDrawText                                                 *
 *                                                                          *
 *  PURPOSE    : Draws psz within lprc in hdc according to wFormat.         *
 *                                                                          *
 *  RETURNS    : Nothing.                                                   *
 *                                                                          *
 ****************************************************************************/
VOID MyDrawText(
HDC hdc,
LPRECT lprc,
PTSTR psz,
DWORD wFormat)
{
    RECT rc;
    DWORD cx;

    if (psz == NULL || !*psz)
        return; // notin to draw dude.

    SetRect(&rc, 0, 0, 1, 0);
    DrawText(hdc, psz, -1, &rc, DT_CALCRECT | DT_NOCLIP | DT_SINGLELINE);
    cx = min(rc.right - rc.left, lprc->right - lprc->left);
    CopyRect(&rc, lprc);
    switch (wFormat & (DT_LEFT | DT_CENTER | DT_RIGHT)) {
    case DT_LEFT:
        rc.right = rc.left + cx;
        break;

    case DT_CENTER:
        cx = (rc.right - rc.left - cx) / 2;
        rc.right -= cx;
        rc.left += cx;
        break;

    case DT_RIGHT:
        rc.left = rc.right - cx;
        break;
    }
    DrawText(hdc, psz, -1, &rc, wFormat | DT_VCENTER);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : InfoCtrlWndProc                                            *
 *                                                                          *
 *  PURPOSE    : Main window proc for info controls                         *
 *                                                                          *
 *  RETURNS    : case dependent                                             *
 *                                                                          *
 ****************************************************************************/
LONG  APIENTRY InfoCtrlWndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
    INFOCTRL_DATA *picd;
    INT i;
    RECT rc;
    HDC hdc;

    switch (msg) {
    case WM_CREATE:
        /*
         * Info controls keep their information in the GWL_INFODATA window
         * word.
         */
        SetWindowLong(hwnd, GWL_INFODATA,
                (DWORD)(DWORD)(((LPCREATESTRUCT)lParam)->lpCreateParams));
        break;

    case WM_SIZE:
        /*
         * size the info control, updating the hittest rectangles.
         * The window is only allowed to get so small.
         */
        if ((short)LOWORD(lParam) < 2 * cxMargin || (short)HIWORD(lParam) < 2 * cyMargin) {
            MoveWindow(hwnd, 0, 0, max((short)LOWORD(lParam), 2 * cxMargin),
                max((short)HIWORD(lParam), 2 * cyMargin), TRUE);
        } else {
            picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
            SetRect(&picd->rcFocusUL, 0, 0, cxMargin, cyMargin);
            SetRect(&picd->rcFocusUR, (short)LOWORD(lParam) - cxMargin, 0,
                    (short)LOWORD(lParam), cyMargin);
            SetRect(&picd->rcFocusLL, 0, (short)HIWORD(lParam) - cyMargin,
                    cxMargin, (INT)HIWORD(lParam));
            SetRect(&picd->rcFocusLR, picd->rcFocusUR.left, picd->rcFocusLL.top,
                    picd->rcFocusUR.right, picd->rcFocusLL.bottom);
        }
        break;

    case WM_DESTROY:
        /*
         * Info control death:
         *
         * Inform out parent - last chance to access GWL_USER.
         * Free our information if it still exists.
         * Free all strings associated with this control.
         */
        {
            PTSTR *ppsz;

            SendMessage(GetParent(hwnd), ICN_BYEBYE, (WPARAM)hwnd,
                    GetWindowLong(hwnd, GWL_USER));
            SetWindowLong(hwnd, GWL_USER, 0);

            picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
            if (picd) {
                ppsz = &picd->pszUL;
                for (i = 0; i < 5; i++, ppsz++) {
                    if (*ppsz) {
                        LocalUnlock((HANDLE)*ppsz);
                        *ppsz = (PTSTR)LocalFree((HANDLE)*ppsz);
                    }
                }
                LocalUnlock((HANDLE)picd);
                LocalFree((HANDLE)picd);
                SetWindowLong(hwnd, GWL_INFODATA, 0);
            }
        }
        break;

    case WM_SETFOCUS:
    case WM_KILLFOCUS:
        /*
         * When focus changes:
         *
         * Alter our look apropriately
         * Bring ourselves to the top if necessary.
         * Inform our parent.
         * Repaint the focus portion of ourselves.
         * Call DefWindowProc()
         */
        picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
        if (picd != NULL) {
            if (picd->style & ICSTY_SHOWFOCUS) {
                if (msg == WM_SETFOCUS)
                    picd->style |= ICSTY_HASFOCUS;
                else
                    picd->style &= ~ICSTY_HASFOCUS;
                BringWindowToTop(hwnd);
                // notify parent
                SendMessage(GetParent(hwnd), ICN_HASFOCUS,
                        msg == WM_SETFOCUS, (LPARAM)hwnd);
            } else {
                picd->style &= ~ICSTY_HASFOCUS;
            }
            hdc = GetDC(hwnd);
            DrawFocus(hdc, hwnd, picd->style);
            ReleaseDC(hwnd, hdc);
        }
        goto DoDWP;
        break;

    case WM_MOUSEMOVE:
        /*
         * Keep the cursor updated to show sizing or moving state.
         */
        {
            LPTSTR cursor;

            picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
            if (picd->style & ICSTY_SHOWFOCUS) {

                if ((INT)HIWORD(lParam) < cyMargin) {
                    if ((short)LOWORD(lParam) < cxMargin) {
                        cursor = IDC_SIZENWSE;
                    } else if ((short)LOWORD(lParam) > picd->rcFocusUR.left) {
                        cursor = IDC_SIZENESW;
                    } else {
                        cursor = IDC_SIZENS;
                    }
                } else if ((INT)HIWORD(lParam) > picd->rcFocusLL.top) {
                    if ((short)LOWORD(lParam) < cxMargin) {
                        cursor = IDC_SIZENESW;
                    } else if ((short)LOWORD(lParam) > picd->rcFocusUR.left) {
                        cursor = IDC_SIZENWSE;
                    } else {
                        cursor = IDC_SIZENS;
                    }
                } else {
                    if ((short)LOWORD(lParam) < cxMargin) {
                        cursor = IDC_SIZEWE;
                    } else if ((short)LOWORD(lParam) > picd->rcFocusUR.left) {
                        cursor = IDC_SIZEWE;
                    } else {
                        cursor = IDC_CROSS;
                    }
                }
            } else {
                cursor = IDC_ARROW;
            }
            SetCursor(LoadCursor(NULL, cursor));
        }
        break;

    case WM_LBUTTONDOWN:
        /*
         * Track window according do mouse location.
         */
        picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
        if (picd->style & ICSTY_SHOWFOCUS) {
            DWORD fs = 0;

            if (!(picd->style & ICSTY_HASFOCUS)) {
                SetFocus(hwnd);
            }

            if ((short)HIWORD(lParam) < cyMargin) {
                fs = TF_TOP;
            } else if ((INT)HIWORD(lParam) > picd->rcFocusLL.top) {
                fs = TF_BOTTOM;
            }
            if ((short)LOWORD(lParam) < cxMargin) {
                fs |= TF_LEFT;
            } else if ((short)LOWORD(lParam) > picd->rcFocusUR.left) {
                fs |= TF_RIGHT;
            } else if (fs == 0) {
                fs = TF_MOVE;
            }

            GetClientRect(hwnd, &rc);
            ClientToScreen(hwnd, (LPPOINT)&rc.left);
            ClientToScreen(hwnd, (LPPOINT)&rc.right);
            ScreenToClient(GetParent(hwnd), (LPPOINT)&rc.left);
            ScreenToClient(GetParent(hwnd), (LPPOINT)&rc.right);
            if (TrackRect(picd->hInst, GetParent(hwnd),
                    rc.left, rc.top, rc.right, rc.bottom,
                    2 * cxMargin, 2 * cyMargin,
                    fs | TF_ALLINBOUNDARY, &rc)) {

                MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left,
                        rc.bottom - rc.top, TRUE);
            }
        }
        break;

    case ICM_SETSTRING:
        /*
         * This message is sent when a info control string value is changeing.
         *
         * wParam = ICSID_ constant
         * lParam = new string.
         *
         * If new string is different from old, free old and allocate space
         * for new one and copy in.
         * Redraw invalidated part of info control.
         */
        {
            PTSTR *ppsz;

            picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
            ppsz = (PTSTR *)&picd->pszUL + wParam;

            if (lParam == 0)
                lParam = (DWORD)(LPTSTR)szNULL;

            if (*ppsz) {
                if (!_tcscmp(*ppsz, (LPTSTR)lParam)) {
                    return 0;
                }
                LocalUnlock((HANDLE)*ppsz);
                *ppsz = (PTSTR)LocalFree((HANDLE)*ppsz);
            }
            if (lParam) {
                *ppsz = (PTSTR)LocalAlloc(LPTR,
                    (_tcslen((LPTSTR)lParam) + 1) * sizeof(TCHAR));
                _tcscpy((LPTSTR)*ppsz, (LPTSTR)lParam);
            }
            GetClientRect(hwnd, &rc);
            switch (wParam) {
            case ICSID_UL:
            case ICSID_UC:
            case ICSID_UR:
                rc.bottom = cyMargin;
                break;

            case ICSID_LL:
            case ICSID_LC:
            case ICSID_LR:
                rc.top = rc.bottom - cyMargin;
                break;

            case ICSID_CENTER:
                InflateRect(&rc, -cxMargin, -cyMargin);
                break;
            }
            InvalidateRect(hwnd, &rc, TRUE);
            UpdateWindow(hwnd);
        }
        break;

    case WM_PAINT:
        /*
         * Paint ourselves.
         *
         * Draw frame.
         * Draw info strings.
         * Send ownerdraw message to parent if ICSTY_OWNERDRAW.
         */
        {
            PAINTSTRUCT ps;
            HANDLE brush;

            picd = (INFOCTRL_DATA *)GetWindowLong(hwnd, GWL_INFODATA);
            BeginPaint(hwnd, &ps);
            // erasure should have already been done for us.
            GetClientRect(hwnd, &rc);
            brush = GetStockObject(BLACK_BRUSH);
            InflateRect(&rc, -cxMargin / 2, -cyMargin / 2);
            FrameRect(ps.hdc, &rc, brush);
            InflateRect(&rc, cxMargin / 2, cyMargin / 2);
            SetRect(&rc, picd->rcFocusUL.right, 0, picd->rcFocusUR.left,
                    cyMargin);
            MyDrawText(ps.hdc, &rc, picd->pszUR, DT_RIGHT);
            MyDrawText(ps.hdc, &rc, picd->pszUL, DT_LEFT);
            MyDrawText(ps.hdc, &rc, picd->pszUC, DT_CENTER);
            SetRect(&rc, picd->rcFocusLL.right, picd->rcFocusLL.top,
                    picd->rcFocusLR.left, picd->rcFocusLR.bottom);
            MyDrawText(ps.hdc, &rc, picd->pszLR, DT_RIGHT);
            MyDrawText(ps.hdc, &rc, picd->pszLL, DT_LEFT);
            MyDrawText(ps.hdc, &rc, picd->pszLC, DT_CENTER);

            GetClientRect(hwnd, &rc);
            InflateRect(&rc, -cxMargin, -cyMargin);
            if (picd->style & ICSTY_OWNERDRAW) {
                OWNERDRAWPS odps;

                if (IntersectRect(&odps.rcPaint, &rc, &ps.rcPaint)) {
                    if (IntersectClipRect(ps.hdc, rc.left, rc.top, rc.right,
                            rc.bottom) != NULLREGION) {
                        odps.rcBound = rc;
                        odps.hdc = ps.hdc;
                        odps.dwUser = GetWindowLong(hwnd, GWL_USER);
                        SendMessage(GetParent(hwnd), ICN_OWNERDRAW,
                                GetWindowLong(hwnd, GWL_ID), (DWORD)(LPTSTR)&odps);
                    }
                }
            } else {
                MyDrawText(ps.hdc, &rc, picd->pszCenter, DT_LEFT | DT_WORDBREAK | DT_EXPANDTABS);
            }
            DrawFocus(ps.hdc, hwnd, picd->style);
            EndPaint(hwnd, &ps);
        }
        break;

DoDWP:
    default:
        return (DefWindowProc(hwnd, msg, wParam, lParam));
    }
    return (0);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : DrawFocus                                                  *
 *                                                                          *
 *  PURPOSE    : To draw focus part of info control.                        *
 *                                                                          *
 *  RETURNS    : nothing                                                    *
 *                                                                          *
 ****************************************************************************/
VOID DrawFocus(
HDC hdc,
HWND hwnd,
DWORD style)
{
    RECT rc;

    GetClientRect(hwnd, &rc);
    FrameRect(hdc, &rc, style & ICSTY_HASFOCUS ?
            hFocusBrush : GetStockObject(GRAY_BRUSH));
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : CountWindows                                               *
 *                                                                          *
 *  PURPOSE    : Counts how many info controls the parent of this window has*
 *                                                                          *
 *  RETURNS    : the count.                                                 *
 *                                                                          *
 ****************************************************************************/
INT CountWindows(
register HWND hwndParent)
{
  INT cWindows = 0;
  register HWND hwnd;

  for (hwnd=GetWindow(hwndParent, GW_CHILD);
        hwnd;
        hwnd= GetWindow(hwnd, GW_HWNDNEXT)) {
      cWindows++;
  }
  return(cWindows);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : GetCascadeWindowPos                                        *
 *                                                                          *
 *  PURPOSE    : Based on a window index and the parent window size,        *
 *               calculates where to place a cascaded window.               *
 *                                                                          *
 *  RETURNS    : rectangle in lprc.                                         *
 *                                                                          *
 ****************************************************************************/
VOID GetCascadeWindowPos(
HWND hwndParent,
INT  iWindow,
LPRECT lprc)
{
  RECT      rc;
  INT       cStack;
  register INT dxClient, dyClient;

  /* Compute the width and breadth of the situation. */
  GetClientRect(hwndParent, (LPRECT)&rc);
  dxClient = rc.right - rc.left;
  dyClient = rc.bottom - rc.top;

  /* How many windows per stack? */
  cStack = dyClient / (3 * cyMargin);

  lprc->right = dxClient - (cStack * cxMargin);
  lprc->bottom = dyClient - (cStack * cyMargin);

  cStack++;             /* HACK!: Mod by cStack+1 */

  lprc->left = (iWindow % cStack) * cxMargin;
  lprc->top = (iWindow % cStack) * cyMargin;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : MyCascadeChildWindows                                      *
 *                                                                          *
 *  PURPOSE    : Cascades all children of a parent window                   *
 *                                                                          *
 *  RETURNS    : nothing                                                    *
 *                                                                          *
 ****************************************************************************/
VOID MyCascadeChildWindows(
register HWND hwndParent)
{
  INT       i;
  INT       cWindows;
  RECT      rc;
  DWORD      wFlags;
  register HWND hwndMove;
  HANDLE    hDefer;

  cWindows = CountWindows(hwndParent);

  if (!cWindows)
      return;

  hwndMove = GetWindow(hwndParent, GW_CHILD);

  hDefer = BeginDeferWindowPos(cWindows);

  for (i=0; i < cWindows; i++) {
      GetCascadeWindowPos(hwndParent, i, (LPRECT)&rc);

      wFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS;

      /* Size the window. */
      hDefer = DeferWindowPos(hDefer,
                 hwndMove, NULL,
                 rc.left, rc.top,
                 rc.right, rc.bottom,
                 wFlags);

      hwndMove = GetWindow(hwndMove, GW_HWNDNEXT);
  }

  EndDeferWindowPos(hDefer);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : TileChildWindows                                           *
 *                                                                          *
 *  PURPOSE    : Tiles all children of a parent window                      *
 *                                                                          *
 *  RETURNS    : nothing.                                                   *
 *                                                                          *
 ****************************************************************************/
VOID TileChildWindows(
register HWND hwndParent)
{
  INT       i;
  INT       dx;
  INT       dy;
  INT       xRes;
  INT       yRes;
  INT       iCol;
  INT       iRow;
  INT       cCols;
  INT       cRows;
  INT       cExtra;
  INT       cWindows;
  register HWND hwndMove;
  RECT      rcClient;
  HANDLE    hDefer;
  DWORD      wFlags;

  cWindows = CountWindows(hwndParent);

  if (!cWindows)
      return;

  /* Compute the smallest nearest square. */
  for (i=2; i * i <= cWindows; i++);

  cRows = i - 1;
  cCols = cWindows / cRows;
  cExtra = cWindows % cRows;

  GetClientRect(hwndParent, (LPRECT)&rcClient);
  xRes = rcClient.right - rcClient.left;
  yRes = rcClient.bottom - rcClient.top;

  if (xRes<=0 || yRes<=0)
      return;

  hwndMove = GetWindow(hwndParent, GW_CHILD);

  hDefer = BeginDeferWindowPos(cWindows);

  for (iCol=0; iCol < cCols; iCol++) {
      if ((cCols-iCol) <= cExtra)
      cRows++;

      for (iRow=0; iRow < cRows; iRow++) {
          dx = xRes / cCols;
          dy = yRes / cRows;

          wFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS;

          /* Position and size the window. */
          hDefer = DeferWindowPos(hDefer, hwndMove, NULL,
                     dx * iCol,
                     dy * iRow,
                     dx,
                     dy,
                     wFlags);

          hwndMove = GetWindow(hwndMove, GW_HWNDNEXT);
      }

      if ((cCols-iCol) <= cExtra) {
          cRows--;
          cExtra--;
      }
  }

  EndDeferWindowPos(hDefer);

}


INFOCTRL.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// STYLES

#define ICSTY_OWNERDRAW     0x0001    // set if the central information is not
                                      // standard text.
#define ICSTY_SHOWFOCUS     0x0002    // set to allow focus painting an movement

#define ICSTY_HASFOCUS      0x8000

#define ICN_OWNERDRAW       (WM_USER + 676)     // notifies to draw
            // wParam=id, lParam=OWNERDRAWPS FAR *
#define ICN_HASFOCUS        (WM_USER + 677)     // notifies of focus set
            // wParam=fFocus, lParam=(hMemCtrlData, hwnd)
#define ICN_BYEBYE          (WM_USER + 678)     // notifies of imminent death
            // wParam=hwnd, lParam=dwUser

#define ICM_SETSTRING       (WM_USER + 776)     // alters a string
            // wParam=index, lParam=LPSTR

#define ICSID_UL            0
#define ICSID_UC            1
#define ICSID_UR            2
#define ICSID_LL            3
#define ICSID_LC            4
#define ICSID_LR            5
#define ICSID_CENTER        6

#define GWL_USER            0
#define GWL_INFODATA        4
#define ICCBWNDEXTRA        8

HWND CreateInfoCtrl(
LPTSTR szTitle,
INT x,
INT y,
INT cx,
INT cy,
HWND hwndParent,
HANDLE hInst,
LPTSTR pszUL,                // NULLs here are fine.
LPTSTR pszUC,
LPTSTR pszUR,
LPTSTR pszLL,
LPTSTR pszLC,
LPTSTR pszLR,
DWORD  style,
HMENU id,
DWORD dwUser);

VOID MyCascadeChildWindows(HWND hwndParent);
VOID TileChildWindows(HWND hwndParent);

typedef struct {
    PTSTR pszUL;
    PTSTR pszUC;
    PTSTR pszUR;
    PTSTR pszLL;
    PTSTR pszLC;
    PTSTR pszLR;
    PTSTR pszCenter;
    DWORD  style;
    RECT rcFocusUL;
    RECT rcFocusUR;
    RECT rcFocusLL;
    RECT rcFocusLR;
    HANDLE hInst;
} INFOCTRL_DATA;

typedef struct {
    RECT rcBound;
    RECT rcPaint;
    HDC  hdc;
    DWORD dwUser;
} OWNERDRAWPS;


MEM.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  MODULE      : mem.c                                                    *
 *                                                                         *
 *  PURPOSE     : Functions for debugging memory allocation bugs.          *
 *                                                                         *
 ***************************************************************************/
#include <windows.h>

#define MAX_OBJECTS 200

PTSTR aptrs[MAX_OBJECTS];
DWORD cptrs = 0;

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : DbgAlloc()                                                 *
 *                                                                          *
 *  PURPOSE    : Useful routine for catching memory allocation errors.      *
 *               Enters allocated objects into an array to check when freed *
 *                                                                          *
 *  RETURNS    : pointer to object allocated.                               *
 *                                                                          *
 ****************************************************************************/
PTSTR DbgAlloc(
register DWORD cb)
{
    register PTSTR p;

    p = (PTSTR)LocalAlloc(LPTR, cb);
    aptrs[cptrs++] = p;
    if (cptrs >= MAX_OBJECTS)
        OutputDebugString(TEXT("Too many objects to track"));
    return p;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : DbgFree()                                                  *
 *                                                                          *
 *  PURPOSE    : To free an object allocated with DbgAlloc().  Checks the   *
 *               object array to make sure an object isn't freed twice.     *
 *                                                                          *
 *  RETURNS    :                                                            *
 *                                                                          *
 ****************************************************************************/
PTSTR DbgFree(
register PTSTR p)
{
    register DWORD i;

    if (p == NULL)
        return p;

    for (i = 0; i < cptrs; i++) {
        if (aptrs[i] == p) {
            aptrs[i] = aptrs[cptrs - 1];
            break;
        }
    }
    if (i == cptrs) {
        OutputDebugString(TEXT("Free on non-allocated object"));
        DebugBreak();
    } else {
        LocalUnlock((HANDLE)p);
        p = (PTSTR)LocalFree((HANDLE)p);
    }
    cptrs--;
    return p;
}


TRACK.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  MODULE      : track.c                                                  *
 *                                                                         *
 *  PURPOSE     : Generic tracking code.                                   *
 *                                                                         *
 ***************************************************************************/
#include <windows.h>
#include "track.h"

RECT  rcTrack;
RECT  rcDelta;
POINT ptOrg;
POINT ptPrev;
DWORD  fsTrack;
RECT  rcBoundary;
INT cxMinTrack;
INT cyMinTrack;

VOID DrawTrackRect(HWND hwnd, LPRECT prcOld, LPRECT prcNew);
VOID HorzUpdate(HDC hdc, INT yOld, INT yNew, INT x1Old, INT x1New, INT x2Old,
        INT x2New);
VOID VertUpdate(HDC hdc, INT xOld, INT xNew, INT y1Old, INT y1New, INT y2Old,
        INT y2New);
LONG  APIENTRY TrackingWndProc(HWND hwnd, DWORD msg, WPARAM wParam, LPARAM lParam);

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : TrackRect()                                                *
 *                                                                          *
 *  PURPOSE    : Implements functionality similiar to the PM WinTrackRect() *
 *                                                                          *
 *  RETURNS    : TRUE on success, FALSE if tracking was canceled.           *
 *               prcResult contains the resulting rectangle.                *
 *                                                                          *
 ****************************************************************************/
BOOL TrackRect(
HANDLE hInst,
HWND   hwnd,        // bounding window
INT    left,        // rectangle to track in bounding window coords.
INT    top,
INT    right,
INT    bottom,
INT    cxMin,
INT    cyMin,
DWORD   fs,
LPRECT prcResult)   // result rect in bounding window coords.
{
    static BOOL fTracking = 0;
    FARPROC lpOrgWndProc, lpTrackWndProc;
    HWND hwndOldCapture, hwndOldFocus;
    MSG msg;

    if (fTracking)
        return FALSE;

    fTracking = TRUE;

    lpOrgWndProc = (FARPROC)GetWindowLong(hwnd, GWL_WNDPROC);
    lpTrackWndProc = MakeProcInstance((FARPROC)TrackingWndProc, hInst);
    SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)lpTrackWndProc);

    hwndOldCapture = GetCapture();
    SetCapture(hwnd);

    hwndOldFocus = SetFocus(hwnd);
    UpdateWindow(hwnd);

    GetCursorPos(&ptOrg);
    ScreenToClient(hwnd, &ptOrg);

    if (fs & TF_SETPOINTERPOS) {

        if (fs & TF_LEFT && fs & TF_RIGHT)
            ptOrg.x = (left + right) / 2;
        else if (fs & TF_LEFT)
            ptOrg.x = left;
        else if (fs & TF_RIGHT)
            ptOrg.x = right;

        if (fs & TF_TOP && fs & TF_BOTTOM)
            ptOrg.y = (top + bottom) / 2;
        else if (fs & TF_TOP)
            ptOrg.y = top;
        else if (fs & TF_BOTTOM)
            ptOrg.y = bottom;

        ClientToScreen(hwnd, &ptOrg);
        SetCursorPos(ptOrg.x, ptOrg.y);
        ScreenToClient(hwnd, &ptOrg);
    }

    ptPrev = ptOrg;
    cxMinTrack = cxMin;
    cyMinTrack = cyMin;
    GetClientRect(hwnd, &rcBoundary);
    fsTrack = fs;
    SetRect(&rcTrack, left, top, right, bottom);
    SetRect(&rcDelta, left - ptOrg.x, top - ptOrg.y, right - ptOrg.x,
            bottom - ptOrg.y);
    DrawTrackRect(hwnd, &rcTrack, NULL);

    while (GetMessage(&msg, NULL, 0, 0))
        DispatchMessage(&msg);

    DrawTrackRect(hwnd, &rcTrack, NULL);

    SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)lpOrgWndProc);
    FreeProcInstance(lpTrackWndProc);

    SetFocus(hwndOldFocus);
    SetCapture(hwndOldCapture);
    CopyRect(prcResult, &rcTrack);

    fTracking = FALSE;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : DrawTrackRect()                                            *
 *                                                                          *
 *  PURPOSE    : XOR draws whats needed to move a selection from prcOld to  *
 *               prcNew.  If prcNew == NULL this is considered a            *
 *               first-time draw or last time erase.                        *
 *                                                                          *
 ****************************************************************************/
VOID DrawTrackRect(
HWND hwnd,
LPRECT prcOld,
LPRECT prcNew)
{
    HDC hdc;

    hdc = GetDC(hwnd);
    SetROP2(hdc, R2_NOT);
        // erase/draw the whole thing
        MoveToEx(hdc, prcOld->left, prcOld->top, NULL);
        LineTo(hdc, prcOld->right, prcOld->top);
        LineTo(hdc, prcOld->right, prcOld->bottom);
        LineTo(hdc, prcOld->left, prcOld->bottom);
        LineTo(hdc, prcOld->left, prcOld->top);
        if (prcNew) {
            MoveToEx(hdc, prcNew->left, prcNew->top, NULL);
            LineTo(hdc, prcNew->right, prcNew->top);
            LineTo(hdc, prcNew->right, prcNew->bottom);
            LineTo(hdc, prcNew->left, prcNew->bottom);
            LineTo(hdc, prcNew->left, prcNew->top);
        }
    ReleaseDC(hwnd, hdc);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : TrackingWndProc()                                          *
 *                                                                          *
 *  PURPOSE    : Window procedure that subclasses the given parent window.  *
 *               This handles the mouse tracking and rectangle updates.     *
 *                                                                          *
 ****************************************************************************/
LONG  APIENTRY TrackingWndProc(
HWND hwnd,
DWORD msg,
WPARAM wParam,
LPARAM lParam)
{
    switch (msg) {
    case WM_MOUSEMOVE:
        {
            RECT rcNow, rcTest;

            if (ptPrev.x == (short)LOWORD(lParam) && ptPrev.y == (short)HIWORD(lParam))
                return 0;
            CopyRect(&rcNow, &rcTrack);
            if (fsTrack & TF_LEFT)
                rcNow.left = (short)LOWORD(lParam) + rcDelta.left;
            if (fsTrack & TF_RIGHT)
                rcNow.right = (short)LOWORD(lParam) + rcDelta.right;
            if (fsTrack & TF_TOP)
                rcNow.top = (short)HIWORD(lParam) + rcDelta.top;
            if (fsTrack & TF_BOTTOM)
                rcNow.bottom = (short)HIWORD(lParam) + rcDelta.bottom;

            if (rcNow.left > rcNow.right - cxMinTrack)
                if (fsTrack & TF_LEFT)
                    rcNow.left = rcNow.right - cxMinTrack;
                else
                    rcNow.right = rcNow.left + cxMinTrack;

            if (rcNow.top > rcNow.bottom - cyMinTrack)
                if (fsTrack & TF_TOP)
                    rcNow.top = rcNow.bottom - cyMinTrack;
                else
                    rcNow.bottom = rcNow.top + cyMinTrack;

            if (fsTrack & TF_ALLINBOUNDARY) {
                if ((fsTrack & TF_MOVE) == TF_MOVE) {
                    IntersectRect(&rcTest, &rcNow, &rcBoundary);
                    if (!EqualRect(&rcTest, &rcNow)) {
                        if (rcNow.left < rcBoundary.left)
                            OffsetRect(&rcNow, rcBoundary.left - rcNow.left, 0);
                        if (rcNow.right > rcBoundary.right)
                            OffsetRect(&rcNow, rcBoundary.right - rcNow.right, 0);
                        if (rcNow.top < rcBoundary.top)
                            OffsetRect(&rcNow, 0, rcBoundary.top - rcNow.top);
                        if (rcNow.bottom > rcBoundary.bottom)
                            OffsetRect(&rcNow, 0, rcBoundary.bottom - rcNow.bottom);
                    }
                } else
                    IntersectRect(&rcNow, &rcNow, &rcBoundary);
            }

            if (EqualRect(&rcNow, &rcTrack))
                return 0;

            DrawTrackRect(hwnd, &rcTrack, &rcNow);

            CopyRect(&rcTrack, &rcNow);
            ((ptPrev).x = ((*((POINTS *)&(lParam)))).x, (ptPrev).y = ((*((POINTS *)&(lParam)))).y);
        }
        break;

    case WM_LBUTTONUP:
        SendMessage(hwnd, WM_MOUSEMOVE, wParam, lParam);
        PostMessage(hwnd, WM_QUIT, 0, 0);       // pop out of modal loop
        return 0;
        break;

    default:
    return(DefWindowProc(hwnd, msg, wParam, lParam));
        break;
    }
    return 0;
}


TRACK.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
 * TRACK.H
 *
 * This module implements a general rectangle tracking service
 */

/* TrackRect() flags */

#define TF_LEFT                         0x0001
#define TF_TOP                          0x0002
#define TF_RIGHT                        0x0004
#define TF_BOTTOM                       0x0008
#define TF_MOVE                         0x000F

#define TF_SETPOINTERPOS        0x0010
#define TF_ALLINBOUNDARY        0x0080

BOOL TrackRect(HANDLE hInst, HWND hwnd, INT left, INT top, INT right,
        INT bottom, INT cxMin, INT cyMin, DWORD fs, LPRECT prcResult);


CLOCK.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
 *  CLOCK.C - Windows DDEML Clock
 *
 *  DDE Transactions:
 *  ----------------
 *  Service: Clock
 *  Topic  : Time
 *  Item   : Now
 *
 *  Use Request or Advise to get the time or use Poke to change the time.
 *  Time Format Hour:Minute:Seconds where
 *   Hour    = 0-23
 *   Minute  = 0-59
 *   Seconds = 0-59
 */

#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "windows.h"
#include <ddeml.h>
#include "clock.h"

DWORD   idInst = 0;

CLOCKDISPSTRUCT ClockDisp;

HANDLE  hInst;
HWND    hWindow;

HBRUSH  hbrBackground;
HBRUSH  hbrColorWindow;
HBRUSH  hbrColorBlack;
HBRUSH  hbrForeground;
HFONT   hFont;
HPEN    hpenForeground;
HPEN    hpenBackground;

INT     nLeadZero   = 0;
INT     TimerID     = 1;    /* number used for timer-id */
INT     clockRadius;
INT     HorzRes;
INT     VertRes;

LONG    aspectD;
LONG    aspectN;

CHAR    szBuffer[BUFLEN];    /* buffer for stringtable stuff */
CHAR    szFontFile[20];
CHAR    szIniFile[20];
CHAR    szSection[30];

POINT   clockCenter;

TIME    oTime;

RECT    clockRect;
RECT    rCoordRect;

HDDEDATA CALLBACK DdeCallback(WORD usType, WORD usFmt, HCONV hConv, HSZ hsz1,
        HSZ hsz2, HDDEDATA hData, DWORD lData1, DWORD lData2);
HSZ hszTime, hszNow, hszClock;    /* Hszs for DDEML use */

/*
 *  Function Prototypes
 */

LONG APIENTRY ClockWndProc(register HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

void NEAR PASCAL ClockCreate(HWND hWnd);
BOOL NEAR PASCAL ClockInit(HANDLE hInstance);
void NEAR PASCAL ClockPaint(HWND hWnd, register HDC hDC, INT hint);
void NEAR PASCAL ClockSize(register HWND hWnd,INT newWidth,INT newHeight, WORD SizeWord);
void NEAR PASCAL ClockTimer(HWND hWnd, UINT msg);
void NEAR PASCAL CompClockDim(void);
void NEAR PASCAL CreateTools(void);
void NEAR PASCAL DeleteTools(void);
void NEAR PASCAL DrawBorder(HWND hWnd, register HDC hDC);
void NEAR PASCAL DrawFace(HDC hDC);
void NEAR PASCAL DrawHand(register HDC hDC, INT pos, HPEN hPen, INT scale, INT patMode);
void NEAR PASCAL DrawFatHand(register HDC hDC, INT pos, HPEN hPen, BOOL hHand);
void NEAR PASCAL FormatInit(register HANDLE hInstance, BOOL fModeChange);
void NEAR PASCAL SizeFont(HWND hWnd, INT newHeight, INT newWidth, INT wlen);
void NEAR PASCAL SetMenuBar( HWND hWnd );

/*
 *  CreateTools()
 */

void NEAR PASCAL CreateTools(void)

{
  hbrForeground  = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT));
  hbrColorWindow = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  hbrColorBlack  = CreateSolidBrush(0L);
  hbrBackground  = hbrColorWindow;
  hpenForeground = CreatePen(0, 1, GetSysColor(COLOR_WINDOWTEXT));
  hpenBackground = CreatePen(0, 1, GetSysColor(COLOR_WINDOW));
}

/*
 *  DeleteTools()
 */

void NEAR PASCAL DeleteTools(void)

{
  DeleteObject(hbrForeground);
  DeleteObject(hbrColorWindow);
  DeleteObject(hbrColorBlack);
  DeleteObject(hpenForeground);
  DeleteObject(hpenBackground);
}

/*
 * SizeFont() - size font according to window size
 */

void NEAR PASCAL SizeFont(HWND hWnd, INT newHeight, INT newWidth, INT wlen)
{
register HDC    hDC;
TEXTMETRIC    tm;
LOGFONT    FontStruct;

    hDC = GetDC(hWnd);
    GetTextMetrics(hDC, &tm);

    if (ClockDisp.wFormat == IDM_DIGITAL) {
        if (hFont != NULL)
            DeleteObject(hFont);

        FontStruct.lfUnderline = FALSE;
        FontStruct.lfStrikeOut = FALSE;
        FontStruct.lfItalic    = FALSE;
        FontStruct.lfEscapement = FALSE;
        FontStruct.lfOrientation = FALSE;
        FontStruct.lfOutPrecision = OUT_DEFAULT_PRECIS;
        FontStruct.lfClipPrecision = CLIP_DEFAULT_PRECIS;

        /* Note that the numbers used in this formula depend on the
         * size of the numbers in the fonts which are 12 vert, 7 horz.
        */
        if (!ClockDisp.bIconic)  {
            FontStruct.lfHeight = (SHORT)min(newHeight/2, 45);

            /* The formula at the end is somewhat empirical. */
            FontStruct.lfWidth = (SHORT)min(25, newWidth/(wlen*3/2));

            /* This if is here because of a problem where:  if a clock
             * existed with a maximum size font, any new clock with a
             * smaller client area would get the same font that would not
             * fit in the Window.  So here, if the width does not match
             * maximum font dimensions, don't use the maximum height.
             */
            if (FontStruct.lfWidth != 25 && FontStruct.lfHeight == 45)
                FontStruct.lfHeight = 40;
        }
        else {
            FontStruct.lfHeight = (SHORT)(newHeight/3);
            FontStruct.lfWidth  = (SHORT)(newWidth/5);
        }

        FontStruct.lfCharSet      = ANSI_CHARSET;
        FontStruct.lfQuality      = DRAFT_QUALITY;
        FontStruct.lfWeight      = FW_NORMAL;
        FontStruct.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;

        lstrcpy(FontStruct.lfFaceName, "DIGITAL");

        hFont = CreateFontIndirect(&FontStruct);

        SelectObject(hDC, hFont);
        GetTextMetrics(hDC, &tm);
        SelectObject(hDC, GetStockObject(SYSTEM_FONT));
    }

    ReleaseDC(hWnd, hDC);

    /* Compute placement for digital text. */
    ClockDisp.line1.x = (newWidth) / 2;
    ClockDisp.line1.y = (newHeight - (tm.tmHeight)) / 2;
    ClockDisp.yline2  = ClockDisp.line1.y + tm.tmHeight;
}

/*
 *  CompClockDim() - Recompute the clock's dimensions.
 */

void NEAR PASCAL CompClockDim(void)

{
INT        i;
register INT    tWidth;
register INT    tHeight;

    tWidth = clockRect.right - clockRect.left;
    tHeight = clockRect.bottom - clockRect.top;

    if (tWidth > (INT)((tHeight * aspectD) / aspectN)) {
        i = (INT)((tHeight * aspectD) / aspectN);
        clockRect.left += (tWidth - i) >> 1;
        clockRect.right = clockRect.left + i;
    }
    else {
        i = (INT)((tWidth * aspectN) / aspectD);
        clockRect.top += (tHeight - i) >> 1;
        clockRect.bottom = clockRect.top + i;
    }
}

/*
 *  ClockSize()
 */

void NEAR PASCAL ClockSize(register HWND hWnd,
               INT       newWidth,
               INT       newHeight,
               WORD      SizeWord)
{
    SetRect((LPRECT)&(clockRect), 0, 0, newWidth, newHeight);
    CompClockDim();

    if (SizeWord == SIZEICONIC) {
        /* Update once every 1/2 minute in the iconic state */
        KillTimer(hWnd, TimerID);
        SetTimer(hWnd, TimerID, (WORD)ICON_TLEN, 0L);
        ClockDisp.bIconic = TRUE;
    }
    else if (ClockDisp.bIconic) {
        /* Update every 1/2 second in the opened state. */
        KillTimer(hWnd, TimerID);
        SetTimer(hWnd, TimerID, OPEN_TLEN, 0L);
        ClockDisp.bIconic = FALSE;
    }

    /* Compute where the digital readout should go. */
    SizeFont(hWnd, newHeight, newWidth, ClockDisp.wDigTimeLen);
}

/*
 *  DrawBorder() - Draws a Red Border around either clock.
 */

void NEAR PASCAL DrawBorder(HWND hWnd, register HDC hDC)
{
RECT Rect;
HPEN hPen;

    GetClientRect(hWnd, (LPRECT) &Rect);

    hPen = CreatePen(PS_SOLID, (ClockDisp.bIconic) ? 1 : 2,
               (ClockDisp.bColor)  ? RGB(255,0,0) : RGB(255,255,255));

    SelectObject(hDC, hPen);
    Rectangle(hDC, Rect.left+1, Rect.top+1, Rect.right, Rect.bottom);
    SelectObject(hDC, GetStockObject(BLACK_PEN));

    DeleteObject(hPen);

    /* Draw an external black border on an icon without killing the client area. */
    if (ClockDisp.bIconic) {
        MoveToEx(hDC, Rect.left, Rect.top, NULL);
        LineTo(hDC, Rect.left,  Rect.bottom);
        LineTo(hDC, Rect.right, Rect.bottom);
        LineTo(hDC, Rect.right, Rect.top);
        LineTo(hDC, Rect.left,  Rect.top);
    }
}

/*
 *  DrawFace()
 */

void NEAR PASCAL DrawFace(HDC hDC)
{
INT        i;
RECT       tRect;
INT        blobHeight, blobWidth;

    blobWidth = (INT)((MAXBLOBWIDTH * (LONG)(clockRect.right - clockRect.left)) / HorzRes);
    blobHeight = (INT)((blobWidth * aspectN) / aspectD);

    if (blobHeight < 2)
        blobHeight = 1;

    if (blobWidth < 2)
        blobWidth = 2;

    InflateRect((LPRECT)&clockRect, -(blobHeight >> 1), -(blobWidth >> 1));

    clockRadius = (clockRect.right - clockRect.left-6) >> 1;
    clockCenter.y = clockRect.top + ((clockRect.bottom - clockRect.top) >> 1);
    clockCenter.x = clockRect.left + clockRadius+3;

    for (i=0; i < 60; i++) {
        tRect.top  = (INT)(((LONG)(CirTab[i].cos) * clockRadius) / CLKSCALE + clockCenter.y);
        tRect.left = (INT)(((LONG)(CirTab[i].sin) * clockRadius) / CLKSCALE + clockCenter.x);

        if (i % 5) {
            /* Draw a dot. */
            if (blobWidth > 2 && blobHeight >= 2) {
                tRect.right = tRect.left + 1;
                tRect.bottom = tRect.top + 1;
                FillRect(hDC, (LPRECT)&tRect, hbrForeground);
            }
        }
        else {
            tRect.right = tRect.left + blobWidth;
            tRect.bottom = tRect.top + blobHeight;
            OffsetRect((LPRECT)&tRect, -(blobWidth >> 1) , -(blobHeight >> 1));
            FillRect(hDC, (LPRECT)&tRect, hbrForeground);
        }
    }
    InflateRect((LPRECT)&clockRect, (blobHeight >> 1), (blobWidth >> 1));
}

/*
 *  DrawHand() - Draw the second hand using XOR mode.
 */

void NEAR PASCAL DrawHand(register HDC hDC,
              INT          pos,
              HPEN         hPen,
              INT          scale,
              INT          patMode)
{
INT       radius;

    MoveToEx(hDC, clockCenter.x, clockCenter.y, NULL);
    radius = (INT)(((LONG)clockRadius * scale) / 100);
    SetROP2(hDC, patMode);
    SelectObject(hDC, hPen);

    LineTo(hDC, clockCenter.x + (INT)(((LONG)(CirTab[pos].sin) * (radius)) / CLKSCALE),
        clockCenter.y + (INT)(((LONG)(CirTab[pos].cos) * (radius)) / CLKSCALE));
}

/*
 *  DrawFatHand() - Draws either hour or minute hand.
 */

void NEAR PASCAL DrawFatHand(register HDC hDC, INT pos, HPEN hPen, BOOL hHand)
{
register INT    m;
INT        n;
INT        scale;
POINT      tip;
POINT      stip;

    SetROP2(hDC, R2_COPYPEN);

    SelectObject(hDC, hPen);

    scale = hHand ? 7 : 5;

    n = (pos+15)%60;
    m = (INT)((((LONG)clockRadius*scale) / 100));
    stip.y = (INT)((LONG)(CirTab[n].cos) * m / CLKSCALE);
    stip.x = (INT)((LONG)(CirTab[n].sin) * m / CLKSCALE);

    scale = hHand ? 65 : 80;
    tip.y = (INT)((LONG)(CirTab[pos].cos) * (((LONG)clockRadius * scale) / 100) / CLKSCALE);
    tip.x = (INT)((LONG)(CirTab[pos].sin) * (((LONG)clockRadius * scale) / 100) / CLKSCALE);

    MoveToEx(hDC, clockCenter.x+stip.x, clockCenter.y+stip.y, NULL);
    LineTo(hDC, clockCenter.x+tip.x,  clockCenter.y+tip.y);
    MoveToEx(hDC, clockCenter.x-stip.x, clockCenter.y-stip.y, NULL);
    LineTo(hDC, clockCenter.x+tip.x,  clockCenter.y+tip.y);

    scale = hHand ? 15 : 20;

    n = (pos + 30) % 60;
    m = (INT)(((LONG)clockRadius * scale) / 100);
    tip.y = (INT)((LONG)(CirTab[n].cos) * m / CLKSCALE);
    tip.x = (INT)((LONG)(CirTab[n].sin) * m / CLKSCALE);
    MoveToEx(hDC, clockCenter.x+stip.x, clockCenter.y+stip.y, NULL);
    LineTo(hDC, clockCenter.x+tip.x,  clockCenter.y+tip.y);
    MoveToEx(hDC, clockCenter.x-stip.x, clockCenter.y-stip.y, NULL);
    LineTo(hDC, clockCenter.x+tip.x,  clockCenter.y+tip.y);
}

/*
 *  ClockPaint()
 */

void NEAR PASCAL ClockPaint(HWND hWnd, register HDC hDC, INT hint)
{
INT     hour;
CHAR    *pszTime;
RECT    Rect;
TIME    nTime;
DWORD   rgbCol;
HBRUSH  hBr;

    GetClientRect(hWnd, (LPRECT) &Rect);

    GetTime(&nTime);

    if (ClockDisp.wFormat == IDM_DIGITAL) {  /* Digital Display */
        if (hint == REPAINT || ClockDisp.bIconic) {
            SelectObject(hDC, GetStockObject(BLACK_BRUSH));
            DrawBorder(hWnd, hDC);

            /* Set old values as undefined, so entire clock updated. */
            oTime.hour24 = 25;
            oTime.minute = 60;
            oTime.ampm   = 2;
        }

        if (oTime.hour24 != nTime.hour24) {
            if (ClockDisp.wTimeFormat)
                hour = nTime.hour24;
            else
                hour = nTime.hour12;

            ClockDisp.szDigTime[0] = (CHAR)('0' + hour / 10);
            ClockDisp.szDigTime[1] = (CHAR)('0' + hour % 10);
        }

        if (oTime.minute != nTime.minute) {
            ClockDisp.szDigTime[3]  = (CHAR)('0' + nTime.minute / 10);
            ClockDisp.szDigTime[4]  = (CHAR)('0' + nTime.minute % 10);
        }

        /* Kill Leading zero if needed. */
        if (nLeadZero == 0 && ClockDisp.szDigTime[0] == '0')
            pszTime = ClockDisp.szDigTime + 1;
        else
            pszTime = ClockDisp.szDigTime;

        SetTextColor(hDC, (ClockDisp.bColor) ? RGB(0,255,0) : RGB(255,255,255));
        SetBkColor(hDC, 0L);
        SetTextAlign(hDC, TA_CENTER);

        ClockDisp.wDigTimeLen = (WORD)((ClockDisp.bIconic ? 5 : 8) + ClockDisp.szDigTime - pszTime);

        /* Is the font ready yet? */
        if (hFont == 0 || ClockDisp.bNewFont)  {
            /* Create a suitable font */
            SizeFont(hWnd, Rect.bottom - Rect.top, Rect.right - Rect.left, ClockDisp.wDigTimeLen);
            ClockDisp.bNewFont = FALSE;
        }

        SelectObject(hDC, hFont);
        ClockDisp.szDigTime[6] = (CHAR)('0' + nTime.second / 10);
        ClockDisp.szDigTime[7] = (CHAR)('0' + nTime.second % 10);

        Rect.left += 4;
        Rect.right -= 4;
        Rect.top = ClockDisp.line1.y;
        Rect.bottom = ClockDisp.yline2;
        ExtTextOut(hDC, ClockDisp.line1.x, ClockDisp.line1.y, ETO_OPAQUE | ETO_CLIPPED,
          (LPRECT)&Rect, pszTime, (UINT)ClockDisp.wDigTimeLen, (LPINT)NULL);
        SelectObject(hDC, GetStockObject(SYSTEM_FONT));
    }
    else {
        /* Analog display */
        SetBkMode(hDC, TRANSPARENT);
        if (hint == REPAINT) {
            SetBkMode(hDC, OPAQUE);
            /* When switching from Digital to analog, the brush selected
             *  continued to be black; So, the current background is to be
             * selected;
             * Fix for Bug #6385 -- SANKAR -- 11-26-89 */
            SelectObject(hDC, hbrBackground);

            /* Make a temp brush to color the background.  This is to
             * force use of a solid color so the hand motion is painted
             * correctly.
             */
            rgbCol = GetNearestColor(hDC, GetSysColor(COLOR_WINDOW));
            hBr = CreateSolidBrush(rgbCol);

            FillRect(hDC, &Rect, hBr);

            DeleteObject(hBr);

            SetBkMode(hDC, TRANSPARENT);

            DrawBorder(hWnd, hDC);
            DrawFace(hDC);
            DrawFatHand(hDC, oTime.hour * 5 + (oTime.minute / 12), hpenForeground, HHAND);
            DrawFatHand(hDC, oTime.minute, hpenForeground, MHAND);

            if (!ClockDisp.bIconic)       /* Draw the second hand. */
                DrawHand(hDC, oTime.second, hpenBackground, SECONDSCALE, R2_NOT);

            /* NOTE: Don't update oTime in this case! */

            return;
        }
        else if (hint == HANDPAINT) {
            if ((!ClockDisp.bIconic) && nTime.second != oTime.second) /* Erase the old second hand. */
                DrawHand(hDC, oTime.second, hpenBackground, SECONDSCALE, R2_NOT);

            if (nTime.minute != oTime.minute || nTime.hour != oTime.hour) {
                if (ClockDisp.bIconic) {
                    DrawHand(hDC, oTime.minute, hpenBackground, MINUTESCALE, R2_COPYPEN);
                    DrawHand(hDC, oTime.hour * 5 + (oTime.minute / 12), hpenBackground, HOURSCALE, R2_COPYPEN);
                    DrawHand(hDC, nTime.minute, hpenForeground, MINUTESCALE, R2_COPYPEN);
                    DrawHand(hDC, nTime.hour * 5 + (nTime.minute / 12), hpenForeground, HOURSCALE, R2_COPYPEN);
                }
                else {
                    DrawFatHand(hDC, oTime.minute, hpenBackground, MHAND);
                    DrawFatHand(hDC, oTime.hour*5+(oTime.minute/12), hpenBackground, HHAND);
                   DrawFatHand(hDC, nTime.minute, hpenForeground, MHAND);
                    DrawFatHand(hDC, (nTime.hour) * 5 + (nTime.minute / 12), hpenForeground, HHAND );
                }
            }

            if (!ClockDisp.bIconic && nTime.second != oTime.second) /* Draw new second hand */
                DrawHand(hDC, nTime.second, hpenBackground, SECONDSCALE, R2_NOT);
        }
    }
    oTime = nTime;
}

/*
 *  ClockTimer()
 *
 *  msg - timer ID
 *
 * Called by windows to tell CLOCK there has been a time change.
 *
 */

void NEAR PASCAL ClockTimer(HWND hWnd, UINT msg)
{
HDC    hDC;
TIME    nTime;

    GetTime(&nTime);

    /* It's possible to change any part of the system at any time
     * through the Control Panel.  So we check everything.
     */
    if (((nTime.second == oTime.second) || ClockDisp.bIconic) &&
        (nTime.minute == oTime.minute)          &&
        (nTime.hour24 == oTime.hour24))
        return;

    hDC = GetDC(hWnd);
    ClockPaint(hWnd, hDC, HANDPAINT);
    ReleaseDC(hWnd, hDC);
    DdePostAdvise(idInst, hszTime, hszNow);
      UNREFERENCED_PARAMETER(msg);
}

/*
 *  ClockCreate()
 */

void NEAR PASCAL ClockCreate(HWND hWnd)
{
INT        i;
register HDC    hDC;
INT        HorzSize;
INT        VertSize;

    hDC = GetDC(hWnd);
    VertRes = GetDeviceCaps(hDC, VERTRES);
    HorzRes = GetDeviceCaps(hDC, HORZRES);
    VertSize= GetDeviceCaps(hDC, VERTSIZE);
    HorzSize= GetDeviceCaps(hDC, HORZSIZE);
    ReleaseDC(hWnd, hDC);

    aspectN = ((LONG)VertRes * 100) / (LONG)VertSize;
    aspectD = ((LONG)HorzRes * 100) / (LONG)HorzSize;

    CreateTools();

    /* Scale sines for aspect ratio if this is the first instance */
    for (i=0; i < 60; i++) {
        CirTab[i].sin = (SHORT)((CirTab[i].sin * aspectN) / aspectD);
    }
}

/*
 *  FormatInit() -  International initialization.
 */

void NEAR PASCAL FormatInit(register HANDLE hInstance, BOOL fModeChange)
{
WORD i, ii;
CHAR szWinHeader[21], szKeyName[21], szRetVal[21];

    for (i=0; i < 11; i++)
        ClockDisp.szDigTime[i] = ' ';

    LoadString(hInstance, IDS_INTL, (LPSTR)szWinHeader, 20);

    LoadString(hInstance, IDS_ITIME, (LPSTR)szKeyName, 20);
    ClockDisp.wTimeFormat = (WORD)GetProfileInt((LPSTR)szWinHeader, (LPSTR)szKeyName, 0);

    LoadString(hInstance, IDS_S1159, (LPSTR)szKeyName, 20);
    LoadString(hInstance, IDS_1159, (LPSTR)szRetVal, 20);
    i = (WORD)GetProfileString((LPSTR)szWinHeader, (LPSTR)szKeyName, (LPSTR)szRetVal, (LPSTR)&ClockDisp.szAMPM[0][0], 7);

    LoadString(hInstance, IDS_S2359, (LPSTR)szKeyName, 20);
    LoadString(hInstance, IDS_2359, (LPSTR)szRetVal, 20);
    ii = (WORD)GetProfileString((LPSTR)szWinHeader, (LPSTR)szKeyName, (LPSTR)szRetVal, (LPSTR)&ClockDisp.szAMPM[1][0], 7);

    nLeadZero = GetProfileInt((LPSTR)szWinHeader, "iTLzero", 0);

    LoadString(hInstance, IDS_STIME, (LPSTR)szKeyName, 20);
    LoadString(hInstance, IDS_TIMESEP, (LPSTR)szRetVal, 20);

    GetProfileString((LPSTR)szWinHeader, (LPSTR)szKeyName, (LPSTR)szRetVal, (LPSTR)szRetVal, 20);
    ClockDisp.cTimeSep = szRetVal[0];

    ClockDisp.szDigTime[2] = ClockDisp.cTimeSep;
    ClockDisp.szDigTime[5] = ClockDisp.cTimeSep;

    LoadString(hInstance, IDS_USNAME, (LPSTR)szWinHeader, 20);
    LoadString(hInstance, IDS_CLKFORMAT, (LPSTR)szKeyName, 20);

    /* We will read the new mode (DIGITAL/ANALOG) only during init time. */
    if (fModeChange)
    {
        if (GetPrivateProfileInt((LPSTR)szWinHeader, (LPSTR)szKeyName, 1, (LPSTR)szIniFile))
            ClockDisp.wFormat = IDM_ANALOG;
        else
            ClockDisp.wFormat = IDM_DIGITAL;
    }

    if (ClockDisp.wFormat == IDM_ANALOG)
        hbrBackground = hbrColorWindow;
    else
        hbrBackground = hbrColorBlack;

    ClockDisp.wDigTimeLen = 2+1+2+1+2+1;

    if (!ClockDisp.wTimeFormat)
        ClockDisp.wDigTimeLen += ((i > ii) ? (i) : (ii));
}

/*
 *  ClockInit()
 */

BOOL NEAR PASCAL ClockInit(HANDLE hInstance)
{
HDC       hDC;
WNDCLASS  ClockClass;

    FormatInit(hInstance, TRUE);

    ClockClass.style         = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
    ClockClass.lpfnWndProc   = ClockWndProc;
    ClockClass.cbClsExtra    = 0;
    ClockClass.cbWndExtra    = 0;
    ClockClass.hInstance     = hInstance;
    ClockClass.hIcon         = NULL;
    ClockClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
    ClockClass.hbrBackground = (HBRUSH) NULL;
    ClockClass.lpszMenuName  = (LPSTR)"Clock";
    ClockClass.lpszClassName = (LPSTR)"Clock";

    if (!RegisterClass((LPWNDCLASS)&ClockClass))
        return(FALSE);

    LoadString(hInstance, IDS_FONTFILE, (LPSTR)szFontFile, 20);
    AddFontResource(szFontFile);

    /* Check the number of colors that the display is capable of. */
    hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
    ClockDisp.bColor = (GetDeviceCaps(hDC, NUMCOLORS) > 2);
    ClockDisp.bColor = FALSE;
    DeleteDC(hDC);

    ClockDisp.bTmpHide = FALSE;
    ClockDisp.bNewFont = FALSE;
    ClockDisp.bColor   = TRUE;
    ClockDisp.bNewFont = TRUE;

    return(TRUE);
}

/*
 *  SetMenuBar() - places or removes the menu bar, etc.
 *
 *  Based on the flag ClockDisp.bNoTitle (ie: do we want a menu/title
 *  bar or not?), adds or removes the window title and menu bar:
 *    Gets current style, toggles the bits, and re-sets the style.
 *    Must then resize the window frame and show it.
 */

void NEAR PASCAL SetMenuBar( HWND hWnd )
{
    static DWORD wID;
    DWORD   dwStyle;

    dwStyle = GetWindowLong( hWnd, GWL_STYLE );
    if( ClockDisp.bNoTitle ) {
        /* remove caption & menu bar, etc. */
        dwStyle &= ~(WS_DLGFRAME | WS_SYSMENU |
                   WS_MINIMIZEBOX | WS_MAXIMIZEBOX );
        wID = SetWindowLong( hWnd, GWL_ID, 0 );
    }
    else {
        /* put menu bar & caption back in */
        dwStyle = WS_TILEDWINDOW | dwStyle;
        SetWindowLong( hWnd, GWL_ID, wID );
    }
    SetWindowLong( hWnd, GWL_STYLE, dwStyle );
    SetWindowPos( hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
        SWP_NOZORDER | SWP_FRAMECHANGED );
    ShowWindow( hWnd, SW_SHOW );
}

/*
 *  AboutDlgProc()
 */

BOOL APIENTRY AboutDlgProc ( hwnd, msg, wParam, lParam )
HWND          hwnd;
UINT msg;
WPARAM wParam;
LPARAM lParam;
{
    switch (msg) {
        case WM_INITDIALOG:
            /* nothing to initialize */
            break;

        case WM_COMMAND:
            switch (LOWORD(wParam)) {
                case IDOK:
                case IDCANCEL:
                    EndDialog(hwnd, 0);
                    break;

                default:
                    return FALSE;
            }
            break;

        default:
            return(FALSE);
    }

    return TRUE;
}

/*
 *  ClockWndProc()
 */

LONG APIENTRY ClockWndProc(register HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HMENU       hMenu;
static CHAR szAppName[12];  /* application name buffer */
PAINTSTRUCT ps;

    switch (message) {
        case WM_COMMAND:
        switch (wParam)  {

            case IDM_ANALOG:
            case IDM_DIGITAL:
                if ((WORD)wParam != ClockDisp.wFormat) {
                    /* Switch flag to other choice */
                    hMenu = GetMenu(hWnd);
                    CheckMenuItem(hMenu, ClockDisp.wFormat, MF_BYCOMMAND | MF_UNCHECKED);
                    CheckMenuItem(hMenu, ClockDisp.wFormat = (WORD) wParam, MF_BYCOMMAND | MF_CHECKED);
                    InvalidateRect(hWnd, (LPRECT) NULL, TRUE);
                }
            break;

            case IDM_NOTITLE:
                goto toggle_title;

            case IDM_ABOUT: {
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUT), hWnd, (DLGPROC)AboutDlgProc);
                break;
            }

            default:
                goto defproc;
        }
        break;

        case WM_SIZE:
            ClockDisp.bNewFont = TRUE;
            ClockSize(hWnd, LOWORD(lParam), HIWORD(lParam), (WORD)wParam);
            UpdateWindow(hWnd);
            break;

        case WM_QUERYDRAGICON:
            return (LONG)LoadIcon(hInst, "cckk");

        case WM_CLOSE:
        case WM_ENDSESSION:
          DestroyWindow( hWnd );
          break;

        case WM_DESTROY: {
            CHAR           szInt[10];
            HCURSOR        hTempCursor;

            KillTimer(hWnd, TimerID);
            DeleteTools();
            if (hFont)
                DeleteObject(hFont);
            RemoveFontResource(szFontFile);

            SetCapture(hWnd);
            hTempCursor=SetCursor(LoadCursor(NULL, IDC_WAIT));

            if (!(IsIconic(hWnd) || IsZoomed(hWnd)))
                GetWindowRect(hWnd, &rCoordRect);

            /* Write new configuration to DDEMLCLK.INI */
            LoadString(hInst, IDS_CLKFORMAT, (LPSTR)szBuffer, BUFLEN-1);
            szInt[0] = (CHAR)('0' + (ClockDisp.wFormat == IDM_ANALOG));
            szInt[1] = 0;
            WritePrivateProfileString((LPSTR)szSection,
                                      (LPSTR)szBuffer,
                                      (LPSTR)szInt,
                                      (LPSTR)szIniFile);
            wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)IsIconic(hWnd));
            WritePrivateProfileString((LPSTR)szSection,
                                      (LPSTR)"Minimized",
                                      (LPSTR)szInt,
                                      (LPSTR)szIniFile);
            wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)IsZoomed(hWnd));
            WritePrivateProfileString((LPSTR)szSection,
                                      (LPSTR)"Maximized",
                                      (LPSTR)szInt,
                                      (LPSTR)szIniFile);
            wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)rCoordRect.left);
            WritePrivateProfileString((LPSTR)szSection,
                                      (LPSTR)"Left",
                                      (LPSTR)szInt,
                                      (LPSTR)szIniFile);
            wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)rCoordRect.top);
            WritePrivateProfileString((LPSTR)szSection,
                                      (LPSTR)"Top",
                                      (LPSTR)szInt,
                                      (LPSTR)szIniFile);
            wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)rCoordRect.right);
            WritePrivateProfileString((LPSTR)szSection,
                                      (LPSTR)"Right",
                                      (LPSTR)szInt,
                                      (LPSTR)szIniFile);
            wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)rCoordRect.bottom);
            WritePrivateProfileString((LPSTR)szSection,
                                      (LPSTR)"Bottom",
                                      (LPSTR)szInt,
                                      (LPSTR)szIniFile);
            wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)ClockDisp.bTopMost);
            WritePrivateProfileString((LPSTR)szSection,
                                      (LPSTR)"TopMost",
                                      (LPSTR)szInt,
                                      (LPSTR)szIniFile);
            wsprintf((LPSTR)szInt, (LPSTR)"%i", (INT)ClockDisp.bNoTitle);
            WritePrivateProfileString((LPSTR)szSection,
                                      (LPSTR)"NoTitle",
                                      (LPSTR)szInt,
                                      (LPSTR)szIniFile);
            PostQuitMessage(0);
            break;
        }

        case WM_WININICHANGE:
            /* FALSE indicates that we don't want to change the display format */
            FormatInit(hInst, FALSE);
            InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
            break;

        case WM_PAINT:
            /* Added to force total repaint to solve
             * problem of garbage under second hand when hidden
             * by menu or popup.
             */
            InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
            BeginPaint(hWnd, (LPPAINTSTRUCT)&ps);

            if (ClockDisp.wFormat == IDM_DIGITAL) {
                hbrBackground = hbrColorBlack;
                FillRect(ps.hdc, (LPRECT)&clockRect, hbrBackground);
            }
            else
                hbrBackground=hbrColorWindow;

            ClockPaint(hWnd, ps.hdc, REPAINT);
            EndPaint(hWnd, (LPPAINTSTRUCT)&ps);
            break;

        case WM_TIMECHANGE:
            /* Redraw. */
            InvalidateRect(hWnd, (LPRECT)NULL, TRUE);

        case WM_TIMER:
            ClockTimer(hWnd, (WORD)wParam);
            break;

        case WM_SYSCOMMAND:
            switch (wParam)  {

                case SC_MINIMIZE:
                    if (!IsZoomed(hWnd))
                        GetWindowRect(hWnd, (LPRECT)&rCoordRect);
                    ClockDisp.bMinimized = TRUE;
                    ClockDisp.bMaximized = FALSE;
                    break;
                case SC_MAXIMIZE:
                    if (!IsIconic(hWnd))
                        GetWindowRect(hWnd, (LPRECT)&rCoordRect);
                    ClockDisp.bMinimized = FALSE;
                    ClockDisp.bMaximized = TRUE;
                    break;

                case IDM_TOPMOST: {
                    /* toggles topmost option
                     */
                    hMenu = GetSystemMenu(hWnd, FALSE);
                    if( ClockDisp.bTopMost )  {
                        CheckMenuItem( hMenu, IDM_TOPMOST, MF_BYCOMMAND | MF_UNCHECKED );
                        SetWindowPos( hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
                                       SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
                        ClockDisp.bTopMost = FALSE;
                    }
                    else {
                        CheckMenuItem( hMenu, IDM_TOPMOST, MF_BYCOMMAND | MF_CHECKED );
                        SetWindowPos( hWnd, HWND_TOPMOST, 0, 0, 0, 0,
                                         SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
                        ClockDisp.bTopMost = TRUE;
                    }
                    break;
                }

            }
            return(DefWindowProc(hWnd, message, wParam, lParam));
            break;

        case WM_MOUSEACTIVATE:
            /* right button temporarily hides the window if topmost is
             * enabled (window re-appears when right button is released).
             * When this happens, we don't want to activate the clock window
             * just before hiding it (it would look really bad), so we
             * intercept the activate message.
             */
            if( GetAsyncKeyState( VK_RBUTTON ) & 0x8000 )
                return( MA_NOACTIVATE );
            else
                goto defproc;
            break;

        case WM_RBUTTONDOWN:
        case WM_NCRBUTTONDOWN:
            /* right button temporarily hides the window, if the window
             * is topmost, and if no menu is currently "active"
             */
            if( !ClockDisp.bTmpHide && ClockDisp.bTopMost ) {
                ShowWindow( hWnd, SW_HIDE );
                SetCapture( hWnd );
                ClockDisp.bTmpHide = TRUE;
            }
            break;

        case WM_RBUTTONUP:
        case WM_NCRBUTTONUP:
            /* if window is currently hidden, right button up brings it
             * back. Must make sure we show it in its previous state - ie:
             * minimized, maximized or normal.
             */
            if( ClockDisp.bTmpHide ) {
                ReleaseCapture();
                if( IsIconic(hWnd) )
                    ShowWindow( hWnd, SW_SHOWMINNOACTIVE );
                else if( IsZoomed( hWnd ) )
                    ShowWindow( hWnd, SW_SHOWMAXIMIZED );
                else
                    ShowWindow( hWnd, SW_SHOWNOACTIVATE );
                ClockDisp.bTmpHide = FALSE;
            }
            break;

        case WM_KEYDOWN:
            /* ESC key toggles the menu/title bar (just like a double click
             * on the client area of the window.
             */
            if( (wParam == VK_ESCAPE) && !(HIWORD( lParam ) & 0x4000) )
                goto toggle_title;
            break;

        case WM_NCLBUTTONDBLCLK:
            if( !ClockDisp.bNoTitle )
                /* if we have title bars etc. let the normal stuff take place */
                goto defproc;

            /* else: no title bars, then this is actually a request to bring
             * the title bars back...
             */

            /* fall through */

        case WM_LBUTTONDBLCLK:
toggle_title:
            ClockDisp.bNoTitle = (ClockDisp.bNoTitle ? FALSE : TRUE );
            SetMenuBar( hWnd );
           break;

        case WM_NCHITTEST:
            /* if we have no title/menu bar, clicking and dragging the client
             * area moves the window. To do this, return HTCAPTION.
             * Note dragging not allowed if window maximized, or if caption
             * bar is present.
             */
            wParam = DefWindowProc(hWnd, message, wParam, lParam);
            if( ClockDisp.bNoTitle && (wParam == HTCLIENT) && !IsZoomed(hWnd) )
                return HTCAPTION;
            else
              return wParam;

        case WM_SYSCOLORCHANGE:
            DeleteTools();
            CreateTools();
            break;

        case WM_ERASEBKGND: {
            RECT rect;

            GetClientRect(hWnd, (LPRECT)&rect);
            SelectObject((HDC)wParam, hbrBackground);
            FillRect((HDC)wParam, (LPRECT)&rect, hbrBackground);
            break;
        }

        default:
defproc:
        return(DefWindowProc(hWnd, message, wParam, lParam));
    }
    return(0L);
}

/*
 *  WinMain()
 */

INT PASCAL WinMain(
HINSTANCE hInstance,
HINSTANCE hPrev,
LPSTR lpszCmdLine,
INT cmdShow)
{
register HWND hWnd;
MSG           msg;
TIME          nTime;
PSTR          szTooMany;
HMENU         hMenu;
CHAR          szTopmost[80];

    LoadString(hInstance, IDS_USNAME, (LPSTR)szBuffer, BUFLEN);
    LoadString(hInstance, IDS_INIFILE, (LPSTR)szIniFile, 20);
    LoadString(hInstance, IDS_USNAME, (LPSTR)szSection, 30);

    if (!ClockInit(hInstance))
        return(FALSE);

    ClockCreate((HWND)NULL);

    LoadString(hInstance, IDS_APPNAME, (LPSTR)szBuffer, BUFLEN);

    rCoordRect.top=GetPrivateProfileInt((LPSTR)szSection,
                                         (LPSTR)"Top",
                                         (DWORD)-1, (LPSTR)szIniFile);
    rCoordRect.left=GetPrivateProfileInt((LPSTR)szSection,
                                         (LPSTR)"Left",
                                         (DWORD)-1, (LPSTR)szIniFile);
    rCoordRect.right=GetPrivateProfileInt((LPSTR)szSection,
                                         (LPSTR)"Right",
                                         (DWORD)-1, (LPSTR)szIniFile);
    rCoordRect.bottom=GetPrivateProfileInt((LPSTR)szSection,
                                         (LPSTR)"Bottom",
                                         (DWORD)-1, (LPSTR)szIniFile);

    hWnd = CreateWindow((LPSTR)"Clock",    /* The class name.           */
            (LPSTR)szBuffer,               /* The window instance name. */
            WS_TILEDWINDOW,
            (rCoordRect.left < 0) ? CW_USEDEFAULT : rCoordRect.left,
            (rCoordRect.top  < 0) ? CW_USEDEFAULT : rCoordRect.top,
            (rCoordRect.left < 0) ? (INT)( (HorzRes/3) + GetSystemMetrics(SM_CXFRAME)*2 )
                  : rCoordRect.right - rCoordRect.left,
            (rCoordRect.left < 0) ? (INT)( (((HorzRes/3)*aspectN)/aspectD)+GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME)*2 )
                  : rCoordRect.bottom - rCoordRect.top,
            NULL,
            NULL,
            hInstance,
            (LPSTR)NULL);

    hWindow=hWnd;

    // Loop if control panel time being changed.
    GetTime((TIME *)&nTime);
    do {
        GetTime((TIME *)&oTime);
    } while (nTime.second == oTime.second && nTime.minute == oTime.minute &&
           nTime.hour24 == oTime.hour24);

    if (!SetTimer(hWnd, TimerID, OPEN_TLEN, 0L) )  {
        /* Windows only supports 16 public timers */
        szTooMany = (PSTR)LocalAlloc(LPTR, 160);
        LoadString(hInstance, IDS_TOOMANY, (LPSTR)szTooMany, 160);
        MessageBox((HWND)NULL, (LPSTR)szTooMany, (LPSTR)szBuffer, MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
        DeleteTools();
        return(FALSE);
    }

    /* Add the topmost system menu item */
    hMenu = GetSystemMenu( hWnd, FALSE );
    AppendMenu( hMenu, MF_SEPARATOR, 0, NULL );
    LoadString(hInstance, IDS_TOPMOST, szTopmost, 79);

    /* Check the default setting to the clock as topmost or not */
    ClockDisp.bTopMost=GetPrivateProfileInt((LPSTR)szSection,
                                         (LPSTR)"TopMost",
                                         0, (LPSTR)szIniFile);
    if( ClockDisp.bTopMost ) {
        AppendMenu( hMenu, MF_ENABLED | MF_CHECKED | MF_STRING, IDM_TOPMOST,
                    szTopmost );
        SetWindowPos( hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
    }
    else
        AppendMenu( hMenu, MF_ENABLED | MF_UNCHECKED | MF_STRING, IDM_TOPMOST,
                    szTopmost );

    /* Check the default menu item either analog or digital */
    CheckMenuItem(GetMenu(hWnd), ClockDisp.wFormat, MF_BYCOMMAND | MF_CHECKED);

    /* Check the default setting to show title bar or not */
    ClockDisp.bNoTitle=GetPrivateProfileInt((LPSTR)szSection,
                                         (LPSTR)"NoTitle",
                                         0, (LPSTR)szIniFile);
    if( ClockDisp.bNoTitle ) {
        SetMenuBar( hWnd );
    }

    hInst = hInstance;

    /* Check the default minimized state, minimized or not */
    ClockDisp.bMinimized=GetPrivateProfileInt((LPSTR)szSection,
                                         (LPSTR)"Minimized",
                                         0, (LPSTR)szIniFile);
    if (!ClockDisp.bMinimized) {
        ClockDisp.bMaximized=GetPrivateProfileInt((LPSTR)szSection,
                                         (LPSTR)"Maximized",
                                         0, (LPSTR)szIniFile);
        if (ClockDisp.bMaximized)
            ShowWindow(hWnd, SW_MAXIMIZE);
        else {
            ShowWindow(hWnd, cmdShow);
            GetWindowRect(hWnd, (LPRECT)&rCoordRect);
        }
    }
    else
        ShowWindow(hWnd, SW_MINIMIZE);

    DdeInitialize(&idInst, (PFNCALLBACK)MakeProcInstance((FARPROC)DdeCallback, hInstance),
            CBF_FAIL_EXECUTES | CBF_SKIP_ALLNOTIFICATIONS, 0L);
    hszTime = DdeCreateStringHandle(idInst, "Time", 0);
    hszNow = DdeCreateStringHandle(idInst, "Now", 0);
    hszClock = DdeCreateStringHandle(idInst, "Clock", 0);
    DdeNameService(idInst, hszClock, 0L, DNS_REGISTER);

    while (GetMessage((LPMSG)&msg, NULL, 0, 0) ) {
        TranslateMessage((LPMSG)&msg);
        DispatchMessage((LPMSG)&msg);
    }

    DdeUninitialize(idInst);

    return(msg.wParam);
}

/*
 *  GetTime()
 */

VOID GetTime(
TIME *ptime)
{
    time_t t;
    struct tm *ptm;

    time(&t);
    ptm = localtime(&t);
    ptime->second = ptm->tm_sec;
    ptime->minute = ptm->tm_min;
    ptime->hour12 =
    ptime->hour = ptm->tm_hour > 12 ? ptm->tm_hour - 12 : ptm->tm_hour;
    ptime->hour24 = ptm->tm_hour;
    ptime->ampm = ptm->tm_hour > 12 ? 1 : 0;
}

/*
 *  DdeCallback()
 */

HDDEDATA CALLBACK DdeCallback(
WORD usType,
WORD usFmt,
HCONV hConv,
HSZ hsz1,
HSZ hsz2,
HDDEDATA hData,
DWORD lData1,
DWORD lData2)
{

static HANDLE           hToken;
static TOKEN_PRIVILEGES tp;
static LUID             luid;

    if (usType == XTYP_CONNECT) {
        return((HDDEDATA)TRUE);
    }

    if (usType == XTYP_WILDCONNECT) {
        HDDEDATA hData;
        HSZPAIR FAR *phszp;
        DWORD cb;

        if ((!hsz1 || hsz1 == hszTime) && (!hsz2 || hsz2 == hszClock)) {
            if ((hData = DdeCreateDataHandle(idInst, NULL,
                    2 * sizeof(HSZPAIR), 0L, 0, 0, 0))) {
                phszp = (HSZPAIR FAR *)DdeAccessData(hData, &cb);
                phszp[0].hszSvc = hszClock;
                phszp[0].hszTopic = hszTime;
                phszp[1].hszSvc = phszp[1].hszTopic = 0;
                DdeUnaccessData(hData);
                return(hData);
            }
        }
        return(0);
    }

    if (usFmt == CF_TEXT) {
        CHAR sz[40];

        if (usType == XTYP_ADVSTART || usType == XTYP_ADVSTOP) {
            return((HDDEDATA)TRUE);
        }

        if (hsz1 == hszTime && hsz2 == hszNow) {
            if (usType == XTYP_REQUEST || usType == XTYP_ADVREQ) {

                itoa(oTime.hour, sz, 10);
                strcat(sz, ":");
                itoa(oTime.minute, &sz[strlen(sz)], 10);
                strcat(sz, ":");
                itoa(oTime.second, &sz[strlen(sz)], 10);
                return(DdeCreateDataHandle(idInst, (LPBYTE)sz, strlen(sz) + 1, 0L,
                        hszNow, CF_TEXT, 0));
            }
            if (usType == XTYP_POKE) {
                SYSTEMTIME SysTime;

                DdeGetData(hData, (LPBYTE)sz, 40L, 0L);
                GetLocalTime(&SysTime);
                sscanf(sz, "%2d:%2d:%2d", &SysTime.wHour, &SysTime.wMinute, &SysTime.wSecond);

                /* enable system-time privilege, set time, disable privilege */
                OpenProcessToken( GetCurrentProcess(),
                  TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) ;
                LookupPrivilegeValue( NULL, "SeSystemTimePrivilege", &luid );
                tp.PrivilegeCount           = 1;
                tp.Privileges[0].Luid       = luid;
                tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
                AdjustTokenPrivileges( hToken, FALSE, &tp,
                  sizeof(TOKEN_PRIVILEGES), NULL, NULL );
                SetLocalTime(&SysTime);
                AdjustTokenPrivileges( hToken, TRUE, &tp,
                  sizeof(TOKEN_PRIVILEGES), NULL, NULL );

                DdePostAdvise(idInst, hszTime, hszNow);
                return((HDDEDATA)DDE_FACK);
            }
        }
    }
    return(0);

    UNREFERENCED_PARAMETER(lData1);
    UNREFERENCED_PARAMETER(lData2);
    UNREFERENCED_PARAMETER(hConv);
}


CLOCK.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*--------------------------------------------------------------------------*/
/*  Typedefs and Structures                                                 */
/*--------------------------------------------------------------------------*/

/* Don't change without updating GETTIME.ASM. */
typedef struct tagTIME
  {
    INT     hour;   /* 0 - 11 hours for analog clock */
    INT     hour12; /* 12 hour format */
    INT     hour24; /* 24 hour format */
    INT     minute;
    INT     second;
    INT     ampm;   /* 0 - AM , 1 - PM */
  } TIME;

typedef struct
{
    SHORT sin;
    SHORT cos;
} TRIG;

TRIG CirTab[] = {     // circle sin, cos, table
    { 0,     -7999  },
    { 836,   -7956  },
    { 1663,  -7825  },
    { 2472,  -7608  },
    { 3253,  -7308  },
    { 3999,  -6928  },
    { 4702,  -6472  },
    { 5353,  -5945  },
    { 5945,  -5353  },
    { 6472,  -4702  },
    { 6928,  -4000  },
    { 7308,  -3253  },
    { 7608,  -2472  },
    { 7825,  -1663  },
    { 7956,  -836   },

    { 8000,  0      },
    { 7956,  836    },
    { 7825,  1663   },
    { 7608,  2472   },
    { 7308,  3253   },
    { 6928,  4000   },
    { 6472,  4702   },
    { 5945,  5353   },
    { 5353,  5945   },
    { 4702,  6472   },
    { 3999,  6928   },
    { 3253,  7308   },
    { 2472,  7608   },
    { 1663,  7825   },
    { 836,   7956   },

    { 0,     7999   },
    { -836,  7956   },
    { -1663, 7825   },
    { -2472, 7608   },
    { -3253, 7308   },
    { -4000, 6928   },
    { -4702, 6472   },
    { -5353, 5945   },
    { -5945, 5353   },
    { -6472, 4702   },
    { -6928, 3999   },
    { -7308, 3253   },
    { -7608, 2472   },
    { -7825, 1663   },
    { -7956, 836    },

    { -7999, -0     },
    { -7956, -836   },
    { -7825, -1663  },
    { -7608, -2472  },
    { -7308, -3253  },
    { -6928, -4000  },
    { -6472, -4702  },
    { -5945, -5353  },
    { -5353, -5945  },
    { -4702, -6472  },
    { -3999, -6928  },
    { -3253, -7308  },
    { -2472, -7608  },
    { -1663, -7825  },
    { -836 , -7956  }
};

typedef struct tagCLOCKDISPSTRUCT
  {
    /* Clock display format for main window/icon outut */
    /* either  IDM_ANALOG, or IDM_DIGITAL */
    WORD    wFormat;

    /* Various boolean settings for the clock style
     * and current settings */
    BOOL bMinimized;
    BOOL bMaximized;
    BOOL bTopMost;
    BOOL bNoTitle;
    BOOL bTmpHide;
    BOOL bIconic;
    BOOL bColor;
    BOOL bNewFont;

    /* X and Y offset within client area of window
     * or icon where digital clock will be displayed */
    POINT   line1;

    /* Y offset within icon area to second line where
     * minute will be displayed */
    INT     yline2;

    /* length of the digital clock readout string,
     * required by the text out function.
     * if the time format is 24 hour, this value is
     * set so that the AM/PM indicator is not output */
    WORD    wDigTimeLen;

    /* buffer to store digital time in.  the hour/minute/ampm
     * sections of this buffer are only modified if they
     * change from the previous call. */
    CHAR    szDigTime[20];

    /* buffer to hold the win.ini international indicators
     * for 1159, and 2359 AM/PM 12 hour time format.
     * szAMPM[0] holds AM, szAMPM[1] holds PM indicator */
    CHAR    szAMPM[2][7];

    /* intl time format (like DOS) 0 - 12 hour, 1 - 24 hour */
    WORD    wTimeFormat;

    /* intl time seperator character */
    CHAR    cTimeSep;
  } CLOCKDISPSTRUCT;

/*--------------------------------------------------------------------------*/
/*  Function Templates                                                      */
/*--------------------------------------------------------------------------*/

void NEAR GetTime(TIME *);
void NEAR ConvTime(TIME *);

LONG APIENTRY ClockWndProc(HWND, UINT, WPARAM, LONG);

/*--------------------------------------------------------------------------*/
/*  Constants                                                               */
/*--------------------------------------------------------------------------*/

#define IDD_ABOUT 400

    /* Main Menu ID defines */

#define IDM_ANALOG       1
#define IDM_DIGITAL      2
#define IDM_ABOUT        4
//--------------
#define IDM_TOPMOST      5
#define IDM_NOTITLE      6
//--------------

/* Temp ID for dialogs. */
#define ID_JUNK     0xCACC

    /* String Resource definitions */

#define IDS_APPNAME      1
#define IDS_DATA         2
#define IDS_TOOMANY      3
#define IDS_FONTFILE     4

#define IDS_INTL        16
#define IDS_ITIME       18
#define IDS_S1159       19
#define IDS_S2359       20
#define IDS_STIME       22
#define IDS_CLKFORMAT   23

#define IDS_1159        24
#define IDS_2359        25
#define IDS_TIMESEP             27
#define IDS_USNAME      29
#define IDS_INIFILE     30
//--------------
#define IDS_TOPMOST     31
//--------------

#define HOURSCALE       65
#define MINUTESCALE     80
#define HHAND           TRUE
#define MHAND           FALSE
#define SECONDSCALE     80
#define MAXBLOBWIDTH    25
#define BUFLEN          30
#define REPAINT         0
#define HANDPAINT       1

#define UPDATE                  0
#define REDRAW                  1

#define OPEN_TLEN               450
#define ICON_TLEN               45000

#define CLKSCALE                ((INT)8000)


README.TXT

Sample: DDEML API Demonstration

Summary:

The Install SDK sample provides an example of how to use the DDEML
API's to add groups and program items to the Program Manager. The
program can be run either in interactive or batch mode and the
search path can be specified on the command line.

More Information:

The program can be started from the command line. The following
flags can be used.

   (-/)(sS) <path> Specify the search path. Currently this must be
   single path. The application searches for an environmental variable
   "MSTOOLS". If this variable is not found then the default value is
   c:\mstools\samples.

   (-/)g(G) <name> Specify the name of the group that the items
   should be added to. The default value is 'Sample Applications'.

   (-/)b(B) Specifies that the program should run in batch mode.
   The program will find all of the .exe files in the specified
   path, searching recursively. It will then create the specified
   group and add all of the found executables to the group. The
   program will then exit.

   (-/)(iI) Specifies interactive mode. This is the default. The
   program will find all of the exe files in the specified
   path, searching recursively. The names will be displayed in
   a list with the name that will be use displayed in an additional
   list. A drop down combo box will display a list of the currently
   available groups. The user can type in a new group name if
   desired.

 If the program finds more than 50 executables then after adding 50
items to a group a new group is created using the name currently
selected in the Program Manager Group list with "Part <n>" appended
where <n> starts at 2 and is incremented after every new group is
added.


DDEDLG.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
   ddedlg.c
*/

#include <windows.h>
#include "ddeinst.h"
#include "ddextrn.h"

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

   AboutBoxWndProc

   Function to process messages to the about box.

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

LRESULT APIENTRY AboutBoxWndProc (HWND hwnd, UINT uiMsg, WPARAM wParam,
      LPARAM lParam) {
   switch (uiMsg) {
      case WM_COMMAND: {
         EndDialog (hwnd, TRUE);
         return (TRUE);
      }/*endCase*/
   }/*endSwitch*/
   return (FALSE);
}/* end AboutBoxWndProc */

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

   ProgressDlgWndProc

   Function to process messages to the about box.

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

LRESULT APIENTRY ProgressDlgWndProc (HWND hwnd, UINT uiMsg, WPARAM wParam,
      LPARAM lParam) {
   switch (uiMsg) {
      case WM_DESTROY: {
         return (TRUE);
      }/*endCase*/
   }/*endSwitch*/
   return (FALSE);
}/* end ProgressDlgWndProc */


DDEDLG.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
   ddedlg.h
*/

#define PD_STOPBUTTON               101
#define PD_COPYING                  103
#define PD_SOURCESTRING             105
#define PD_TARGETSTRING             106


DDEGLBL.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
   ddeglbl.h
*/

// Global Declarations
HANDLE           ghModule;
HWND             ghwndMain = (HWND) NULL;
HANDLE           ghAccel;

LPSTR            szUserPath = NULL;
LPSTR            szUserGroup = NULL;

// Window Handle Globals
HWND             hwndStatus;
HWND             hwndStatusText;
HWND             hwndFileList;
HWND             hwndFileList2;
HWND             hwndAddButton;
HWND             hwndAddGroupButton;
HWND             hwndAddAll;
HWND             hwndExitButton;
HWND             hwndCombo;
HWND             hwndPathList;

// Progress Indicator Dialog
HWND             hwndDialog;

// DDEML Globals
LONG             lIdInst;
LONG             lIdInst2;

// Program Flags
BOOL             fBatch = FALSE;


DDEINST.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
   ddeinst.c
*/

#include <windows.h>
#include "ddeinst.h"
#include "ddextrn.h"

#include <stdio.h>
#include <string.h>

extern CONVCONTEXT CCFilter;

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

   StartTraverseThread

   Function that starts thread to traverse the specified directory.

********************************************************************/
BOOL StartTraverseThread (LPSTR lpArg) {
   HANDLE   hThread;
   LONG     lThreadId;

   hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) TraversePath,
      lpArg, CREATE_SUSPENDED | STANDARD_RIGHTS_REQUIRED, &lThreadId);
   if (hThread) {
      // SetThreadPriority (hThread, THREAD_PRIORITY_BELOW_NORMAL);
      ResumeThread(hThread);

/* Close the handle since we don't need it anymore */

      CloseHandle (hThread);
      return (TRUE);
   }/*endIf*/
   return (FALSE);
}/* end StartTraverseThread */

int   iRecurse = 0;

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

   TraversePath

   Function that recursively descends a specified directory looking
   for files that have the .exe extension.

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

BOOL TraversePath (LPSTR szPath) {
   HANDLE            hSearch;
   WIN32_FIND_DATA   findData;
   LPSTR             szTemp;
   LPSTR             szNewPath;
   LPSTR             szTempName;
   LPSTR             szFullPath;
   int               iLen;
   long              lError;
   long              lPos;
   long              lTemp;
   long              lNewPath;

// If there is no path then we need to exit.
   if (!szPath || (strlen (szPath) > (MAX_PATH - 5))) {
      return (FALSE);
   }/*endIf*/

// Increment the recursion level.
   iRecurse++;

// Get the length of the path plus room for the wildcard and nul.
   lTemp = strlen (szPath) + 5;

// allocate some memory
   szTemp = VirtualAlloc (NULL, lTemp, MEM_COMMIT, PAGE_READWRITE);

// Append the wildcard
   sprintf (szTemp, "%s\\*.*", szPath);

// Look for a file
   hSearch = FindFirstFile (szTemp, &findData);

// If nothing found then return.
   if (!hSearch || hSearch == (HANDLE) -1) {
      VirtualFree (szTemp, lTemp, MEM_DECOMMIT);
      iRecurse--;
      return (FALSE);
   }/*endIf*/

// Loop through the current directory looking for exectuables.
   do {
      if (strcmp (findData.cFileName, ".") &&
            strcmp (findData.cFileName, "..")) {

      // Ignore '.' and '..'
         if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {

         // If it is a directory then recurse.

            lNewPath = strlen (szPath) + strlen (findData.cFileName) + 1;
            szNewPath = VirtualAlloc (NULL, lNewPath, MEM_COMMIT,
                  PAGE_READWRITE);
            sprintf (szNewPath, "%s\\%s", szPath, findData.cFileName);
            TraversePath (szNewPath);
            VirtualFree (szNewPath, lNewPath, MEM_DECOMMIT);
         } else {

         // See if it ends in .exe

            iLen = strlen (findData.cFileName) - 4;
            szTempName = findData.cFileName + iLen;
            if (!strnicmp (szTempName, ".exe", 4)) {

            // Add the file name to the list of files.

               if (!(lPos = SendMessage (hwndFileList, LB_ADDSTRING,
                  (WPARAM) NULL, (LPARAM) findData.cFileName))) {

               // Debugging code

                  lError = GetLastError ();
               }/*endIf*/
               szTempName = VirtualAlloc (NULL, iLen + 4, MEM_COMMIT,
                     PAGE_READWRITE);
               strncpy (szTempName, findData.cFileName, iLen);

            // Add the constructed file name to the list of names

               SendMessage (hwndFileList2, LB_INSERTSTRING, (WPARAM) lPos,
                     (LPARAM) szTempName);
               VirtualFree (szTempName, iLen + 4, MEM_DECOMMIT);
               iLen = strlen (szPath) + iLen + 4;

            // Construct a full pathname to the file.

               szFullPath = VirtualAlloc (NULL, iLen, MEM_COMMIT,
                     PAGE_READWRITE);
               sprintf (szFullPath, "%s\\%s", szPath, findData.cFileName);

            // Add the full path to the hidden list.

               SendMessage (hwndPathList, LB_INSERTSTRING, (WPARAM) lPos,
                     (LPARAM) szFullPath);
               VirtualFree (szFullPath, iLen, MEM_DECOMMIT);
            }/*endIf*/
         }/*endIf*/
      }/*endIf*/

   // Keep looping until no more files.

   } while (FindNextFile (hSearch, &findData));

// End the search.

   FindClose (hSearch);
   VirtualFree (szTemp, lTemp, MEM_DECOMMIT);

// Decrement the recursion count.

   iRecurse--;
   if (!iRecurse) {

   // If recursion count == 0 then we are done so inform the primary thread.

      PostMessage (ghwndMain, WM_USER_THREAD_DONE, 0, 0L);
   }/*endIf*/
   return (TRUE);
}/* end TraversePath */

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

   StartGroupRetrievalThread

   Function that starts thread to retrieve the names of existing groups
   from the Program Manager.

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

BOOL StartGroupRetrievalThread () {
   HANDLE   hThread;
   LONG     lThreadId;

   hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) GroupRetrieval,
      "test", CREATE_SUSPENDED | STANDARD_RIGHTS_REQUIRED, &lThreadId);
   if (hThread) {
      SetThreadPriority (hThread, THREAD_PRIORITY_BELOW_NORMAL);
      ResumeThread (hThread);

/* Close the handle since we don't need it anymore */

      CloseHandle (hThread);
      return (TRUE);
   }/*endIf*/
   return (FALSE);
}/* end StartGroupRetrievalThread */

// Prototype for DDECallback function.
HDDEDATA CALLBACK GroupDDECallback (UINT, UINT, HANDLE, HSZ, HSZ, HDDEDATA,
      LONG, LONG);

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

   GroupRetrieval
   Function to obtain the names of all of the defined program groups in
   the Program Manager.

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

BOOL GroupRetrieval (LPSTR lpDummy) {
   LPBYTE     lpByte;
   LPSTR      szGroups;
   LPSTR      szToken;
   LPSTR      szMem;
   LPSTR      szToken2;
   long       lResult;
   LONG       lIdLocal;
   CRITICAL_SECTION lpCritical;
   HCONV      hConv;
   HDDEDATA   hDdeData;
   HSZ        szProgMan;
   HSZ        szTopic;

// Initialize the Dde id to 0

   lIdLocal = 0L;
   szGroups = NULL;

// Attempt to initialize a conversation.

   if (DdeInitialize (&lIdLocal, (PFNCALLBACK) GroupDDECallback,
         (DWORD) APPCMD_CLIENTONLY, 0L)) {
      return (FALSE);
   }/*endIf*/

// Start a critical section. This fixes a problem where the DDEML
// Can hang under threaded conditions.

   InitializeCriticalSection (&lpCritical);
   EnterCriticalSection (&lpCritical);

// Create a string handle for the Dde conversation.

   szProgMan = DdeCreateStringHandle (lIdLocal, "PROGMAN", CP_WINANSI);

// Connect to the program manager.

   hConv = DdeConnect (lIdLocal, szProgMan, szProgMan, &CCFilter);

   PostMessage (hwndStatus, WM_USER_UPDATE_STATUS, 0, ID_DDEML_CONNECT);

// Create a handle for the Group topic.

   szTopic = DdeCreateStringHandle (lIdLocal, "Groups", CP_WINANSI);

// Execute a request for the group names.

   hDdeData = DdeClientTransaction (NULL, 0L, hConv, szTopic, CF_TEXT,
         XTYP_REQUEST, 1000, &lResult);

   PostMessage (hwndStatus, WM_USER_UPDATE_STATUS, 0, ID_DDEML_RETRIEVING);
// Release the two string handles.

   DdeFreeStringHandle (lIdLocal, szProgMan);
   DdeFreeStringHandle (lIdLocal, szTopic);

// Disconnect the conversation.

   DdeDisconnect (hConv);

   PostMessage (hwndStatus, WM_USER_UPDATE_STATUS, 0, ID_DDEML_COMPLETE);

// Exit and delete the critical section.

   LeaveCriticalSection (&lpCritical);
   DeleteCriticalSection (&lpCritical);

// Verify that we got something back.

   if (hDdeData && (hDdeData != (HDDEDATA) TRUE)) {

   // Access the data to get a character pointer.

      lpByte = DdeAccessData (hDdeData, &lResult);

   // allocate a block of memory to use.

      szGroups = szMem = VirtualAlloc (NULL, lResult, MEM_COMMIT,
         PAGE_READWRITE);
      if (!szMem) {
         lResult = GetLastError ();
      } else {

      // Copy the Dde data to our own block.

         memcpy (szGroups, lpByte, lResult);
      }/*endIf*/

   // Free the Dde data that is there. This is our responsibility.

      DdeUnaccessData (hDdeData);
      DdeFreeDataHandle (hDdeData);
   }/*endIf*/

// If we have a memory block then parse it.

   if (szMem != NULL) {

   // Search for 'cr'.

      szToken = strtok (szGroups, "\n");

   // While we have a token use it.

      while (szToken) {

      // We have to strip out line feeds.

         if (szToken2 = strrchr (szToken, 0x0d)) {
            *szToken2 = '\0';
         }/*endIf*/

      // Add the resulting string to the combo box of groups.

         SendMessage (hwndCombo, CB_ADDSTRING, (WPARAM) 0, (LPARAM) szToken);

      // Find the next 'cr'.

         szToken = strtok (NULL, "\n");
      }/*endWhile*/

   // Release the memory block.

      VirtualFree (szMem, lResult, MEM_DECOMMIT);
   }/*endIf*/

// If there is a default name defined then use it. And enable the AddGroup
// Button.

   if (strlen (szUserGroup)) {
      SetWindowText (hwndCombo, szUserGroup);
      EnableWindow (hwndAddGroupButton, TRUE);
   } else {

   // There is no default group so disable the button.

      EnableWindow (hwndAddGroupButton, FALSE);
   }/*endIf*/

// Release the DDEML.

   DdeUninitialize (lIdLocal);

// Inform the primary thread that the group retrieval is completed.

   PostMessage (ghwndMain, WM_USER_THREAD_DONE, 0, 0L);
   return (TRUE);
}/* end GroupRetrieval */

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

   GroupDDECallback.

   Function exists for compatibility. Does nothing.

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

HDDEDATA CALLBACK GroupDDECallback (UINT uiType, UINT uiFmt, HANDLE hConv,
      HSZ sz1, HSZ sz2, HDDEDATA hData, LONG lData1, LONG lData2) {
   switch (uiType) {

   }/*endSwitch*/
   return ((HDDEDATA) NULL);
}/* end GroupDDECallback */


DDEINST.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
   ddeinst.h -- include file for DDEML Installer.
*/

// Window Procedure function prototypes.
LRESULT APIENTRY    MainWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT APIENTRY    PathDialogWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT APIENTRY    ProgressDialogWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT APIENTRY    StatusBarWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT APIENTRY    ListBoxWndProc (HWND, UINT, WPARAM, LPARAM);
WNDPROC             DefListBoxWndProc;

// Utility functions
BOOL                DecodeOptions (LPSTR);
BOOL                InitializeApp (void);
void                CleanUpApp (void);

// Path traversal functions
BOOL                StartTraverseThread (LPSTR);
BOOL                TraversePath (LPSTR);

// DDEML Group information retrieval functions
BOOL                StartGroupRetrievalThread (void);
BOOL                GroupRetrieval ();

// DDEML Execute functions
BOOL                StartAddThread (void);
BOOL                CreateGroup (void);
BOOL                AddProgramItems (LPSTR);
HDDEDATA CALLBACK   GroupDDECallback (UINT, UINT, HANDLE, HSZ, HSZ, HDDEDATA,
                          LONG, LONG);

// Dialog Window Procedures
LRESULT APIENTRY    AboutBoxWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT APIENTRY    ProgressDlgWndProc (HWND, UINT, WPARAM, LPARAM);

#define ACCEL_ID        1024
#define APPICON         2048

// The Menu identifiers
#define MainMenu        10
#define DI_EXIT         11
#define DI_ABOUT        13

// The Child control identifiers
#define ID_STATUSBAR    2
#define ID_FILELIST     3
#define ID_FILELIST2    4
#define ID_ADDBUTTON    5
#define ID_ADDALLBUTTON 6
#define ID_EXITBUTTON   7
#define ID_COMBOBOX     8
#define ID_PATHLIST     9
#define ID_ADDGROUP     10

// DDEML Starter flags
#define WM_USER_GET_GROUPS      (WM_USER+1)
#define WM_USER_GET_APPS        (WM_USER+2)

// Dialog Box update message
#define WM_USER_UPDATE_DIALOG   (WM_USER+10)
#define WM_USER_CLOSE_DIALOG    (WM_USER+12)
#define WM_USER_THREAD_DONE     (WM_USER+14)

// DDEML Progress message for Status Bar
#define WM_USER_UPDATE_STATUS   (WM_USER+16)

#define ID_DDEML_CONNECT        1
#define ID_DDEML_RETRIEVING     2
#define ID_DDEML_DISCONNECT     3
#define ID_DDEML_COMPLETE       4
#define ID_DDEML_CREATE         5
#define ID_DDEML_ADD            6
#define ID_DDEML_ACTIVATE       7


DDEMAIN.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
   ddemain.c
*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ddeinst.h"
#include "ddeglbl.h"

HMENU   hMenu;

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

   WinMain.

   Function that starts the app.

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

int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst,
      LPSTR lpCmdLine, int iShowCmd) {
   MSG   msg;

   ghModule = GetModuleHandle (NULL);

// Initialize the Application
   if (!InitializeApp ()) {
      MessageBox (ghwndMain, "memory: InitializeApp failure!", "Error", MB_OK);
      return (FALSE);
   }/*endIf*/

// Parse the command line
   if (!DecodeOptions (lpCmdLine)) {
      MessageBox (ghwndMain, "decode: Command line errors!", "Error", MB_OK);
      CleanUpApp ();
      return (FALSE);
   }/*endIf*/

// Load the accelerators
   if (!(ghAccel = LoadAccelerators (ghModule, MAKEINTRESOURCE(ACCEL_ID)))) {
      MessageBox(ghwndMain, "memory: Load Accel failure!", "Error", MB_OK);
   }/*endIf*/

// Test
   PostMessage (ghwndMain, WM_USER_GET_APPS, 0, 0L);

// Get the group names from the Program Manager
   PostMessage (ghwndMain, WM_USER_GET_GROUPS, 0, 0L);

// Main Event Loop
   while (GetMessage (&msg, NULL, 0, 0)) {
      if (!TranslateAccelerator (ghwndMain, ghAccel, &msg)) {
         TranslateMessage (&msg);
         DispatchMessage (&msg);
      }/*endIf*/
   }/*endWhile*/

// Clean up
   CleanUpApp ();
   return (TRUE);
}/* end WinMain */

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

   InitializeApp.

   Function that registers the window classes and creates the main
   window.

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

BOOL InitializeApp () {
   char      szErrorString[80];
   WNDCLASS wc;
   long      lError;

// Fill in the class info for the main window.
   wc.style         = CS_OWNDC;
   wc.lpfnWndProc   = (WNDPROC)MainWndProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = sizeof(LONG);
   wc.hInstance     = ghModule;
   wc.hIcon         = LoadIcon(ghModule, MAKEINTRESOURCE(APPICON));
   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
   wc.lpszMenuName  = "MainMenu";
   wc.lpszClassName = "DDEMLInstaller";

   if (!RegisterClass(&wc))
      return FALSE;

// Fill in the class info for the status bar.
   wc.style         = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = (WNDPROC)StatusBarWndProc;
   wc.hIcon         = NULL;
   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH)(COLOR_BTNSHADOW);
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = "StatusBar";

   if (!RegisterClass(&wc))
      return FALSE;

   hMenu = LoadMenu (ghModule, MAKEINTRESOURCE (MainMenu));
   if (!hMenu) {
      lError = GetLastError ();
      sprintf (szErrorString, "MainMenu load failed %ld", lError);
      MessageBox (GetDesktopWindow (), szErrorString, "Error", MB_OK);
   }/*endIf*/

   ghwndMain = CreateWindowEx (0L, "DDEMLInstaller", "Sample Installer",
         WS_OVERLAPPED   | WS_CAPTION     | WS_BORDER       |
         WS_THICKFRAME   | WS_MAXIMIZEBOX | WS_MINIMIZEBOX  |
         WS_CLIPCHILDREN | WS_SYSMENU,
         80, 70, 550, 250,
         NULL, hMenu, ghModule, NULL);

   if (ghwndMain == NULL)
      return FALSE;

   ShowWindow (ghwndMain, SW_SHOWDEFAULT);
   UpdateWindow (ghwndMain);

   SetWindowLong (ghwndMain, GWL_USERDATA, 0L);

// Set the initial focus to the main window
   SetFocus(ghwndMain);    /* set initial focus */

   return TRUE;
}/* end InitializeApp */

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

   CleanUpApp.

   Function that takes care of house cleaning when app terminates.

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

void CleanUpApp () {

// Destroy the menu loaded in InitializeApp.
   DestroyMenu (hMenu);

// If the DDEML conversation id is non-zero the Uninitialize the conversation
   if (lIdInst2) {
      DdeUninitialize (lIdInst2);
   }/*endIf*/

// If memory has been allocated for the user defined path then release it.
   if (szUserPath) {
      VirtualFree (szUserPath, MAX_PATH, MEM_DECOMMIT);
   }/*endIf*/
   if (szUserGroup) {
      VirtualFree (szUserGroup, MAX_PATH, MEM_DECOMMIT);
   }/*endIf*/
}/* end CleanUpApp */

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

   DecodeOptions.

   Function that decodes the command line looking for the user specified
   arguments.

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

BOOL DecodeOptions (LPSTR lpCmdLine) {
   LPSTR  szFirst;
   LPSTR  szToken;

// Allocate a block of memory to use for the user specified path.
   szUserPath = VirtualAlloc (NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
   szUserGroup = VirtualAlloc (NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
   if (!GetEnvironmentVariable ("MSTOOLS", szUserPath, MAX_PATH - 1)) {
      strcpy (szUserPath, "c:\\mstools\\samples");
   } else {
      strcat (szUserPath, "\\samples");
   }/*endIf*/
   strcpy (szUserGroup, "Sample Applications");
   fBatch = FALSE;
   if (!(szToken = strtok (lpCmdLine, "-/"))) {
      return (TRUE);
   }/*endIf*/
   do {
      switch (*szToken) {
         case 's':
         case 'S':{
            szFirst = szToken + 1;
            while (*szFirst == ' ') {
               szFirst++;
            }/*endWhile*/
            strcpy (szUserPath, szFirst);
            while (szFirst = strrchr (szUserPath, ' ')) {
               *szFirst = '\0';
            }/*endWhile*/
            break;
         }/*endCase*/
         case 'b':
         case 'B': {
            fBatch = TRUE;
            break;
         }/*endCase*/
         case 'i':
         case 'I': {
            fBatch = FALSE;
            break;
         }/*endCase*/
         case 'g':
         case 'G': {
            szFirst = szToken + 1;
            while (*szFirst == ' ') szFirst++;
            strcpy (szUserGroup, szFirst);
            while (szFirst = strrchr (szUserGroup, ' ')) {
               *szFirst = '\0';
            }/*endWhile*/
            break;
         }/*endCase*/
      }/*endSwitch*/
   } while (szToken = strtok (NULL, "-/"));
   return (TRUE);
}/* end DecodeOptions */


DDEPROCS.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
   ddeprocs.c
*/

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include "ddeinst.h"
#include "ddextrn.h"
#include "dialogs.h"

// function prototype for List synchronization
void   SynchronizeLists (HWND);
void   SynchronizeListSelection (HWND);

// function prototype for child window creation
void   CreateChildren (HWND);

// internal thread counter
int    iThreadCount = 0;

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

   MainWndProc.

   Function that processes the messages sent to the main window.

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

LRESULT APIENTRY MainWndProc (HWND hwnd, UINT uiMsg, WPARAM wParam,
      LPARAM lParam) {
   BOOL        fFlag;

   switch (uiMsg) {
      case WM_CREATE: {

// Create all of the child windows that are desired.
         CreateChildren (hwnd);
         break;
      }/*endCase*/
      case WM_DESTROY: {
         PostQuitMessage (0);
         return (0L);
      }/*endCase*/
      case WM_USER_CLOSE_DIALOG: {
         if (hwndDialog) {
            DestroyWindow (hwndDialog);
            hwndDialog = (HWND) NULL;
            return (TRUE);
         }/*endIf*/
         break;
      }/*endCase*/
      case WM_SIZE: {
      /* Need to resize the StatusBar */
         if (hwndStatus) {
            long lHeight;
            int  iWidth;
            int  iHeight;

            iWidth = LOWORD (lParam);
            iHeight = HIWORD (lParam);
            lHeight = GetWindowLong (hwndStatus, GWL_USERDATA);
            MoveWindow (hwndStatus, -1, iHeight - (int) lHeight,
                  iWidth + 2, lHeight + 2, TRUE);
         }/*endIf*/
         break;
      }/*endCase*/
      case WM_USER_GET_APPS: {
         StartTraverseThread (szUserPath);
         return (1L);
      }/*endCase*/
      case WM_USER_GET_GROUPS: {
         StartGroupRetrievalThread ();
         return (1L);
      }/*endCase*/
      case WM_USER_THREAD_DONE: {
         iThreadCount++;
         if (!fBatch) {
            break;
         }/*endIf*/
         if (iThreadCount == 2) {
            PostMessage (ghwndMain, WM_COMMAND,
                  (WPARAM) MAKELONG (ID_ADDALLBUTTON, BN_CLICKED),
                  (LPARAM) hwndAddAll);
            PostMessage (ghwndMain, WM_COMMAND,
                  (WPARAM) MAKELONG (ID_ADDGROUP, BN_CLICKED),
                  (LPARAM) hwndAddGroupButton);
            PostMessage (ghwndMain, WM_COMMAND,
                  (WPARAM) MAKELONG (ID_ADDBUTTON, BN_CLICKED),
                  (LPARAM) hwndAddButton);
            iThreadCount = 0;
         }/*endIf*/
         break;
      }/*endCase*/
      case WM_COMMAND: {
         switch (HIWORD (wParam)) {
            case LBN_SELCHANGE: {
               SynchronizeListSelection ((HWND) lParam);
               if ((HWND) lParam == hwndCombo) {
                  EnableWindow (hwndAddGroupButton, TRUE);
               }/*endIf*/
               break;
            }/*endCase*/
            case CBN_EDITCHANGE: {
               fFlag = (BOOL) GetWindowTextLength (hwndCombo);
               EnableWindow (hwndAddGroupButton, fFlag);
               break;
            }/*endCase*/
            case BN_CLICKED: {
               switch (LOWORD (wParam)) {
                  case ID_ADDALLBUTTON: {

               // Select all elements in the two lists.
               // Under Win 3.x LPARAM of LB_SETSEL is MAKELPARAM (-1, 0)
               // Under Win32 it is -1

                     SendMessage (hwndFileList, LB_SETSEL, (WPARAM) TRUE,
                           (LPARAM) -1);

               // Under Win 3.x LPARAM of LB_SETSEL is MAKELPARAM (-1, 0)
               // Under Win32 it is -1

                     SendMessage (hwndFileList2, LB_SETSEL, (WPARAM) TRUE,
                           (LPARAM) -1);
                     break;
                  }/*endCase*/
                  case ID_EXITBUTTON: {
                     PostQuitMessage (0);
                     break;
                  }/*endCase*/
                  case ID_ADDBUTTON: {
                  // Create the Progress Dialog
                     hwndDialog = CreateDialog (ghModule,
                           "ProgressDialog", hwnd, ProgressDlgWndProc);

                  // Start a thread to add the items
                     StartAddThread ();
                     break;
                  }/*endCase*/
                  case ID_ADDGROUP: {

                  // Add the user defined group.
                     CreateGroup ();
                     break;
                  }/*endCase*/
               }/*endSwitch*/
               break;
            }/*endCase*/
         }/*endSwitch*/

     // Process menu events now.

         switch (LOWORD (wParam)) {
            case DI_EXIT: {
               PostQuitMessage (0);
               return (1L);
            }/*endCase*/
            case DI_ABOUT: {
               DialogBox (ghModule, "AboutBox", hwnd,
                     (DLGPROC) AboutBoxWndProc);
               break;
            }/*endCase*/
         }/*endSwitch*/
         break;
      }/*endCase*/
   }/*endSwitch*/
   return (DefWindowProc (hwnd, uiMsg, wParam, lParam));
}/* end MainWndProc */

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

   StatusBarWndProc.

   Function that manages the Status bar at the bottom of the main
   window.

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

LRESULT APIENTRY StatusBarWndProc (HWND hwnd, UINT uiMsg, WPARAM wParam,
      LPARAM lParam) {
   HDC             hdc;
   long            lHeight;
   RECT            rc;
   PAINTSTRUCT     ps;
   TEXTMETRIC      tm;
   POINT           pts[6];

   switch (uiMsg) {
      case WM_CTLCOLORSTATIC: {
         SetBkMode ((HDC) wParam, TRANSPARENT);
         return ((LRESULT) GetStockObject (WHITE_BRUSH));
      }/*endCase*/
      case WM_CREATE: {
         hdc = GetDC (hwnd); // Get the DC for text metrics

         GetClientRect (GetParent (hwnd), &rc);
         GetTextMetrics (hdc, &tm);

      // Calculate the height of the window
         lHeight = tm.tmHeight + 10 * GetSystemMetrics (SM_CYBORDER) + 2;

      // Save away the calculated height
         SetWindowLong (hwnd, GWL_USERDATA, lHeight);

      //* resize the window
         SetWindowPos (hwnd, NULL, -1, rc.bottom - lHeight,
            rc.right + 2, lHeight + 2, SWP_NOZORDER | SWP_NOMOVE);

         ReleaseDC (hwnd, hdc);
         break;
      }/*endCase*/
      case WM_USER_UPDATE_STATUS: {
         switch (lParam) {
            case ID_DDEML_CONNECT: {
               SetWindowText (hwndStatusText, "DDEML Connection");
               break;
            }/*endCase*/
            case ID_DDEML_RETRIEVING: {
               SetWindowText (hwndStatusText, "DDEML Retrieving");
               break;
            }/*endCase*/
            case ID_DDEML_DISCONNECT: {
               SetWindowText (hwndStatusText, "DDEML Disconnect");
               break;
            }/*endCase*/
            case ID_DDEML_COMPLETE: {
               SetWindowText (hwndStatusText, "DDEML Complete");
               break;
            }/*endCase*/
            case ID_DDEML_ACTIVATE: {
               SetWindowText (hwndStatusText, "DDEML Activate Group");
               break;
            }/*endCase*/
            case ID_DDEML_CREATE: {
               SetWindowText (hwndStatusText, "DDEML Create Group");
               break;
            }/*endCase*/
            case ID_DDEML_ADD: {
               TCHAR   szText[32];

               sprintf (szText, "DDEML Add Item %ld", wParam);
               SetWindowText (hwndStatusText, szText);
               break;
            }/*endCase*/
         }/*endSwitch*/
         break;
      }/*endCase*/
      case WM_PAINT: {
         HBRUSH   hBrush;
         HBRUSH   hBrushTemp;
         HPEN     hPen;
         HPEN     hPenTemp;

         hdc = BeginPaint (hwnd, &ps);
         GetClientRect (hwnd, &rc);

         lHeight = GetWindowLong (hwnd, GWL_USERDATA);
         pts[0].x = 3;
         pts[0].y = pts[5].y = lHeight - 4;
         pts[1].x = 6;
         pts[1].y = pts[2].y = lHeight - 6;
         pts[2].x = pts[3].x = (rc.right / 2) - 4;
         pts[3].y = 6;
         pts[4].y = 3;
         pts[4].x = pts[5].x = rc.right / 2;
         hBrush = GetStockObject (WHITE_BRUSH);
         hPen = GetStockObject (WHITE_PEN);
         hBrushTemp = SelectObject (hdc, hBrush);
         hPenTemp = SelectObject (hdc, hPen);
         SetPolyFillMode (hdc, WINDING);
         Polygon (hdc, pts, 6);
         hBrush = CreateSolidBrush (GetSysColor (COLOR_BTNSHADOW));
         pts[2].x = 6;
         hPen = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNSHADOW));
         pts[2].y = 6;
         pts[5].x = pts[5].y = 3;
         SelectObject (hdc, hBrush);
         SelectObject (hdc, hPen);
         Polygon (hdc, pts, 6);
         rc.left = rc.top = 7;
         rc.right = (rc.right / 2) - 4;
         rc.bottom = lHeight - 7;
         SelectObject (hdc, hBrushTemp);
         SelectObject (hdc, hPenTemp);
         DeleteObject (hBrush);
         DeleteObject (hPen);
         hBrush = GetStockObject (WHITE_BRUSH);
         FillRect (hdc, &rc, hBrush);
         DeleteObject (hBrush);
         EndPaint (hwnd, &ps);
         return (0L);
      }/*endCase*/
   }/*endSwitch*/
   return (DefWindowProc (hwnd, uiMsg, wParam, lParam));
}/* end StatusBarWndProc */

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

   ListBoxWndProc.

   Function that manages the list boxes that are on the screen. Subclassed
   to provide additional functionality.

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

LRESULT APIENTRY ListBoxWndProc (HWND hwnd, UINT uiMsg, WPARAM wParam,
      LRESULT lParam) {

   switch (uiMsg) {
      case WM_KEYUP: {
         SynchronizeLists (hwnd);
         break;
      }/*endCase*/
      case WM_VSCROLL: {
         SynchronizeLists (hwnd);
         break;
      }/*endCase*/
      break;
   }/*endSwitch*/
   return (CallWindowProc (DefListBoxWndProc, hwnd, uiMsg, wParam, lParam));
}/* end ListBoxWndProc */

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

   SynchronizeLists.

   Function that makes sure that the two lists are synchronized and
   always have the same selection.

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

void SynchronizeLists (HWND hwnd) {
   HWND   hwndOther;
   long   lSourceTop;
   long   lTargetTop;

// If this is from the combo list then return.
   if (hwnd == hwndCombo) {
      return;
   }/*endIf*/

// Figure out which list caused the event and update the other.
   if (hwnd == hwndFileList) {
      hwndOther = hwndFileList2;
   } else {
      hwndOther = hwndFileList;
   }/*endIf*/

// Get the top index of the source list.
   lSourceTop = SendMessage (hwnd, LB_GETTOPINDEX, 0, 0L);

// Get the top index of the target list.
   lTargetTop = SendMessage (hwndOther, LB_GETTOPINDEX, 0, 0L);

// If the top indexes are different set the top of the target to the
// same value as the source.
   if (lSourceTop != lTargetTop) {
      SendMessage (hwndOther, LB_SETTOPINDEX, lSourceTop, 0L);
   }/*endIf*/
}/* end SynchronizeLists */

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

   SynchronizeListSelection.

   Function that actually manages the list selection.

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

void SynchronizeListSelection (HWND hwnd) {
   HWND   hwndOther;
   long   lNumSel;
   int   * lpSelection;
   long   lIndex;

// If this is from the combo list then return.

   if (hwnd == hwndCombo) {
      return;
   }/*endIf*/

// Figure out which list caused the event and update the other.

   if (hwnd == hwndFileList) {
      hwndOther = hwndFileList2;
   } else {
      hwndOther = hwndFileList;
   }/*endIf*/

// Find out how many items are selected in the source list.

   lNumSel = SendMessage (hwnd, LB_GETSELCOUNT, 0, 0L);
   if (lNumSel != LB_ERR && lNumSel != 0) {

   // Allocate a block of memory to hold the selection indexes.
      lpSelection = (int *) GlobalAlloc (GMEM_FIXED | GMEM_ZEROINIT,
         lNumSel * sizeof (int));
      if (lpSelection) {

   // Clear the selection in the list before reselecting.
   // Under Win 3.x LPARAM of LB_SETSEL is MAKELPARAM (-1, 0)
   // Under Win32 it is -1

         SendMessage (hwndOther, LB_SETSEL, (WPARAM) FALSE, (LPARAM) -1);

   // Retrieve the selection indexes.

         SendMessage (hwnd, LB_GETSELITEMS, lNumSel, (LPARAM) lpSelection);

   // Loop the indexes selecting the same values in the target list.

         for (lIndex = 0; lIndex < lNumSel; lIndex++) {
            SendMessage (hwndOther, LB_SETSEL, (WPARAM) TRUE,
               MAKELPARAM (lpSelection[lIndex], 0));
         }/*endFor*/

   // Release the memory that was allocated.

         GlobalFree (lpSelection);
      }/*endIf*/
   }/*endIf*/
}/* end SynchronizeListSelection */

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

   CreateChildren.

   Function that creates all of the child windows for the app.

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

void CreateChildren (HWND hwnd) {
   int              iListHeight;
   long             lHeight;
   RECT             rc;

// Create the status bar at the bottom of the window.
   hwndStatus = CreateWindow ("StatusBar", NULL,
         WS_BORDER | SS_LEFT | WS_CHILD | WS_VISIBLE,
         0, 0, 0, 0, hwnd, (HMENU) ID_STATUSBAR, ghModule, NULL);

// Retrieve the height of the status bar.
   lHeight = GetWindowLong (hwndStatus, GWL_USERDATA);

// Retrieve the size of the client area of the status bar.
   GetClientRect (hwndStatus, &rc);

   hwndStatusText = CreateWindow ("Static", "Status Bar",
         WS_VISIBLE | WS_CHILD | SS_LEFT,
         7, 7, rc.right / 2 - 10, lHeight - 14, hwndStatus, (HMENU) 0, ghModule,
         NULL);

// Retrieve the size of the client area of the parent window
   GetClientRect (hwnd, &rc);

// Calculate the size of a list box.
   iListHeight = rc.bottom - lHeight * 2;

// Decrease the size of lHeight to compensate for size of status bar.
   lHeight -= 8;

// Create a Static window in the upper left corner.
   CreateWindow ("Static", "Available exe's",
         WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | SS_LEFT,
         0, 0, 200, lHeight - 2, hwnd, (HMENU) 0, ghModule, NULL);

// Create the first list to hold the file name.
   hwndFileList = CreateWindow ("ListBox", NULL, WS_CLIPSIBLINGS | LBS_NOTIFY |
         WS_BORDER | WS_VISIBLE | WS_CHILD | LBS_EXTENDEDSEL | LBS_HASSTRINGS,
         0, lHeight, 150, iListHeight, hwnd, (HMENU) ID_FILELIST, ghModule,
         NULL);

// Subclass the list box window proc so we can do the synchronization
   DefListBoxWndProc = (WNDPROC) GetWindowLong (hwndFileList, GWL_WNDPROC);
   SetWindowLong (hwndFileList, GWL_WNDPROC, (LONG) ListBoxWndProc);

// Create the second list box that holds the name that will be used in the
// Program Manager.
   hwndFileList2 = CreateWindow ("ListBox", NULL,
         WS_BORDER | WS_VISIBLE | WS_CHILD | WS_VSCROLL | LBS_EXTENDEDSEL |
         LBS_NOTIFY | LBS_HASSTRINGS | LBS_SORT | WS_CLIPSIBLINGS, 150,
         lHeight, 150, iListHeight, hwnd, (HMENU) ID_FILELIST2, ghModule,
         NULL);

// Subclass the list.
   SetWindowLong (hwndFileList2, GWL_WNDPROC, (LONG) ListBoxWndProc);

// Create a third list box that is not visible that contains the absolute
// path to the executable.
   hwndPathList = CreateWindow ("ListBox", NULL,
         WS_BORDER | WS_CHILD | WS_VSCROLL | LBS_EXTENDEDSEL |
         LBS_NOTIFY | LBS_HASSTRINGS | LBS_SORT | WS_CLIPSIBLINGS, 150, lHeight,
         150, iListHeight, hwnd, (HMENU) ID_PATHLIST, ghModule, NULL);

// Create a button that is used to add a group.
   hwndAddGroupButton = CreateWindow ("Button", "Add Group",
         WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
         rc.right - 110, iListHeight - (int) (lHeight * 4),
         100, lHeight, hwnd, (HMENU) ID_ADDGROUP, ghModule, NULL);

// Create the button that is used to add the selected items.
   hwndAddButton = CreateWindow ("Button", "Add Items",
         WS_DISABLED | WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
         rc.right - 110, iListHeight - (int) (lHeight * 3),
         100, lHeight, hwnd, (HMENU) ID_ADDBUTTON, ghModule, NULL);

// Create the button that is used to select all of the items in the list.
   hwndAddAll = CreateWindow ("Button", "Select All",
         WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
         rc.right - 110, iListHeight - (int) (lHeight * 2),
         100, lHeight, hwnd, (HMENU) ID_ADDALLBUTTON, ghModule, NULL);

// Create a button that can be used to exit the application.
   hwndExitButton = CreateWindow ("Button", "Exit",
         WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
         rc.right - 110, iListHeight - (int) lHeight, 100, lHeight, hwnd,
         (HMENU) ID_EXITBUTTON, ghModule, NULL);

// Create a drop down combo box for the group names from the Program Manager.
   hwndCombo = CreateWindow ("ComboBox", NULL,
         WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | CBS_DROPDOWN | WS_VSCROLL |
         CBS_AUTOHSCROLL | CBS_HASSTRINGS, rc.right - 200, lHeight,
         190, lHeight * 5, hwnd, (HMENU) ID_COMBOBOX, ghModule, NULL);

// Create a Static window to label the combo box.
   CreateWindow ("Static", "Available Groups",
         WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | SS_LEFT,
         rc.right - 200, 0, 190, lHeight - 2, hwnd, (HMENU) 0, ghModule, NULL);
}/* end CreateChildren */


DDEXTRN.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
   ddeextrn.h
*/

// Global Declarations
extern HANDLE           ghModule;
extern HWND             ghwndMain;
extern HANDLE           ghAccel;

extern LPSTR            szUserPath;
extern LPSTR            szUserGroup;

// Window Handle Globals
extern HWND             hwndStatus;
extern HWND             hwndStatusText;
extern HWND             hwndFileList;
extern HWND             hwndFileList2;
extern HWND             hwndAddButton;
extern HWND             hwndAddGroupButton;
extern HWND             hwndAddAll;
extern HWND             hwndExitButton;
extern HWND             hwndCombo;
extern HWND             hwndPathList;

// Dialog Box handle
extern HWND             hwndDialog;

// DDEML Globals
extern LONG             lIdInst;
extern LONG             lIdInst2;

// Program Flags
extern BOOL             fBatch;


DIALOGS.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
   dialogs.h
*/

#define IDL_DIALOGMAIN              100
#define IDL_EXEPATH                 102
#define IDL_ITEMNAME                104
#define IDL_PROGRESSBAR             105
#define IDL_PERCENTAGE              106


DDEADD.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*

   ddeadd.c
   DDEML Execute functions to add selected items
   and create new group if specified.

*/

#include <windows.h>
#include <stdio.h>
#include <string.h>

#include "ddeinst.h"
#include "ddextrn.h"
#include "dialogs.h"

CONVCONTEXT   CCFilter = { sizeof (CONVCONTEXT), 0, 0, 0, 0L, 0L };

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

   StartAddThread

   Function to Start the program item addition thread.

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

BOOL StartAddThread () {
   HANDLE hThread;
   LONG   lThreadId;

   hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) AddProgramItems,
         "Dummy", CREATE_SUSPENDED | STANDARD_RIGHTS_REQUIRED, &lThreadId);
   if (hThread) {
      SetThreadPriority (hThread, THREAD_PRIORITY_BELOW_NORMAL);
      ResumeThread (hThread);

/* Close the handle since we don't need it anymore */

      CloseHandle (hThread);
      return (TRUE);
   }/*endIf*/
   return (FALSE);
}/* end StartAddThread */

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

   CreateGroup

   Function that creates a group in the Program Manager. If the specified
   name already exists the group is activated.

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

BOOL CreateGroup () {
   HDDEDATA   hData;
   LPSTR      szText;
   LPSTR      szCommand;
   HCONV      hConv;
   HSZ        szProgMan;
   LONG       lResult;

// Allocate a block of memory for the group name that the user has selected.
   szText = VirtualAlloc (NULL, 64, MEM_COMMIT, PAGE_READWRITE);

// Allocate a block of memory for the command to send to the Program Manager.
   szCommand = VirtualAlloc (NULL, 128, MEM_COMMIT, PAGE_READWRITE);
   if (szText) {
   // We got the memory so we can proceed.

   // Initialize the DDE conversation here.

      if (DdeInitialize (&lIdInst, (PFNCALLBACK) GroupDDECallback,
            (DWORD) APPCMD_CLIENTONLY, 0L)) {
         MessageBox (ghwndMain, "DDEML Initialization Failure", "Error", MB_OK);
         VirtualFree (szText, 128, MEM_DECOMMIT);
         return (FALSE);
      }/*endIf*/

   // Create a DDEML string handle for the Program Manager.
      szProgMan = DdeCreateStringHandle (lIdInst, "PROGMAN", CP_WINANSI);
      if (szProgMan) {

      // String handle created so continue.

// Establish a conversation with the Program Manager.
         hConv = DdeConnect (lIdInst, szProgMan, szProgMan, &CCFilter);

         PostMessage (hwndStatus, WM_USER_UPDATE_STATUS, 0,
               ID_DDEML_CONNECT);

// Retrieve the text of the combo control. This will return back what the
// user has typed in if nothing has been selected.
         GetWindowText (hwndCombo, szText, 64);

// Find out if this string is in the list or not. If not create a group
// Otherwise just activate the group.
         lResult = (LONG) SendMessage (hwndCombo, CB_FINDSTRING, (WPARAM) -1,
               (LPARAM) (LPCSTR) szText);
         if (lResult == CB_ERR) {

// Create the group by creating the command line.
// The program manager will activate an existing group if the name matches
// a DDE attempt to create a new group so the create versus show is somewhat
// redundant.
            sprintf (szCommand, "[CreateGroup(%s)]", szText);

// Create a DDEML data handle for the command.
            hData = DdeCreateDataHandle (lIdInst, szCommand,
                  strlen (szCommand) + 1, 0, (HSZ) NULL, CF_TEXT, 0L);

// Send the transaction to the server waiting a maximum of 10 seconds.
// The server will release the data handle.
            if (!DdeClientTransaction ((LPBYTE) hData, 0xFFFFFFFF, hConv,
                  (HSZ) NULL, 0, XTYP_EXECUTE, 10000, &lResult)) {

// If it fails get the error code.
               lResult = DdeGetLastError (lIdInst);
               MessageBox (NULL, "DdeClientTransaction Failed", "Error",
                  MB_OK);
            }/*endIf*/

            PostMessage (hwndStatus, WM_USER_UPDATE_STATUS, 0,
                  ID_DDEML_CREATE);

         } else {

// Group already exists so activate it.
            sprintf (szCommand, "[ShowGroup(%s,1)]", szText);

// Create a DDEML data handle for the command.
            hData = DdeCreateDataHandle (lIdInst, szCommand, strlen (szCommand),
                  0, (HSZ) NULL, CF_TEXT, 0L);

// Send the transaction to the server waiting a maximum of 10 seconds.
// The server will release the data handle.
            if (!DdeClientTransaction ((LPBYTE) hData, 0xFFFFFFFF, hConv,
                  (HSZ) NULL, 0, XTYP_EXECUTE, 10000, &lResult)) {

// If it fails get the error code.
               lResult = DdeGetLastError (lIdInst);
            }/*endIf*/

            PostMessage (hwndStatus, WM_USER_UPDATE_STATUS, 0,
                  ID_DDEML_ACTIVATE);

         }/*endIf*/

// Release the Program Manager string handle.
         DdeFreeStringHandle (lIdInst, szProgMan);

// Disconnect from the server.
         DdeDisconnect (hConv);
         PostMessage (hwndStatus, WM_USER_UPDATE_STATUS, 0,
                  ID_DDEML_COMPLETE);
      } else {
         lResult = DdeGetLastError (lIdInst);
      }/*endIf*/

// Free the two blocks of memory that were allocated.
      VirtualFree (szText, 64, MEM_DECOMMIT);
      VirtualFree (szCommand, 64, MEM_DECOMMIT);
      EnableWindow (hwndAddButton, TRUE);

   // Uninitialize the conversation here so that resources are freed.

      DdeUninitialize (lIdInst);
      lIdInst = 0L;
      return (TRUE);
   } else {
      MessageBox (NULL, "Memory Allocation failure", "Error", MB_OK);
   }/*endIf*/
   EnableWindow (hwndAddButton, FALSE);
   return (FALSE);
}/* end CreateGroup */

// Local function
void UpdateProgressBar (int, int);

// Flag for progress bar update
BOOL   fFirst;

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

   AddProgramItems

   Function that uses DDEML to add program items to group in Program
   Manager.

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

BOOL AddProgramItems (LPSTR szDummy) {
   char      szPercent[8];
   HDDEDATA  hData;
   HCONV     hConv;
   HSZ       szProgMan;
   int       lSelCount;
   LONG      lResult;
   LPLONG    lpSelection;
   LPSTR     szProgName;
   LPSTR     szExePath;
   LPSTR     szExecuteString;
   int       iIndex;
   int       iGroupCount;

   fFirst = FALSE;
   iGroupCount = 2;
// Retrieve the number of selected items from the file list.
   lSelCount = (int) SendMessage (hwndFileList, LB_GETSELCOUNT, 0, 0L);

// Allocate a block of memory to hold the indexes of the list selection.
   lpSelection = VirtualAlloc (NULL, lSelCount * sizeof (int), MEM_COMMIT,
         PAGE_READWRITE);
   if (lpSelection) {

// Establish a new conversation.
      if (DdeInitialize (&lIdInst2, (PFNCALLBACK) GroupDDECallback,
            (DWORD) APPCMD_CLIENTONLY, 0L)) {
         VirtualFree (lpSelection, lSelCount * sizeof (int), MEM_DECOMMIT);
         return (FALSE);
      }/*endIf*/

// Create a DDEML string handle for the Program Manager.
      szProgMan = DdeCreateStringHandle (lIdInst2, "PROGMAN", CP_WINANSI);

// Establish the conversation.
      hConv = DdeConnect (lIdInst2, szProgMan, szProgMan, &CCFilter);

      PostMessage (hwndStatus, WM_USER_UPDATE_STATUS, 0,
           ID_DDEML_CONNECT);

// Release the string handle that we created.
      DdeFreeStringHandle (lIdInst2, szProgMan);

// Allocate a block of memory to hold the path and name of the item.
      szProgName = VirtualAlloc (NULL, MAX_PATH * 2, MEM_COMMIT,
            PAGE_READWRITE);
      szExePath = szProgName + MAX_PATH;

// Allocate some memory to hold the command string.
      szExecuteString = VirtualAlloc (NULL, MAX_PATH * 4, MEM_COMMIT,
            PAGE_READWRITE);

// Retrieve the array of list selections.
      SendMessage (hwndFileList, LB_GETSELITEMS, (WPARAM) lSelCount,
            (LPARAM) lpSelection);

// Loop through the array of selections.
      for (iIndex = 0; iIndex < lSelCount; iIndex++) {

// Work around for limitation for in ProgMan (no more than 50 items per group)
         if (!(iIndex % 50) && (iIndex > 0)) {
            LPSTR      szText;
            LPSTR      szText2;

         // Allocate a block of memory for the group name
            szText = VirtualAlloc (NULL, 64, MEM_COMMIT, PAGE_READWRITE);
            szText2 = VirtualAlloc (NULL, 256, MEM_COMMIT, PAGE_READWRITE);
            if (szText && szText2) {
               GetWindowText (hwndCombo, szText, 64);
               sprintf (szText2, "[CreateGroup(%s Part %ld )]", szText,
                     iGroupCount++);

         // Create a DDEML data handle for the command.
               hData = DdeCreateDataHandle (lIdInst2, szText2,
                  strlen (szText2) + 1, 0, (HSZ) NULL, CF_TEXT, 0L);

         // Send the transaction to the server waiting a maximum of 10 seconds.
         // The server will release the data handle.
               if (!DdeClientTransaction ((LPBYTE) hData, 0xFFFFFFFF, hConv,
                     (HSZ) NULL, 0, XTYP_EXECUTE, 10000, &lResult)) {
         // If it fails get the error code.
                  lResult = DdeGetLastError (lIdInst);
                  MessageBox (NULL, "DdeClientTransaction Failed",
                        "Error", MB_OK);
               }/*endIf*/
               VirtualFree (szText, 64, MEM_DECOMMIT);
               VirtualFree (szText2, 256, MEM_DECOMMIT);
            }
         }/*endIf*/

// Retrieve the Name of the Program Item to add.
         SendMessage (hwndFileList2, LB_GETTEXT, (WPARAM) lpSelection[iIndex],
               (LPARAM) szProgName);

// Set the name in the progress dialog
         SetDlgItemText (hwndDialog, IDL_ITEMNAME, szProgName);

// Retrieve the absolute path of the item to add.
         SendMessage (hwndPathList, LB_GETTEXT, (WPARAM) lpSelection[iIndex],
               (LPARAM) szExePath);

// Set the path in the progress dialog
         SetDlgItemText (hwndDialog, IDL_EXEPATH, szExePath);

// Set the percentage in the progress dialog
         sprintf (szPercent, "%d%%", (iIndex * 100) / lSelCount);
         SetDlgItemText (hwndDialog, IDL_PERCENTAGE, szPercent);
         UpdateProgressBar (iIndex + 1, lSelCount);

// Create the command string to add the item.
         sprintf (szExecuteString, "[AddItem(%s,%s)]", szExePath,
               (LPARAM) szProgName);

// Create a DDEML Data handle for the command string.
         hData = DdeCreateDataHandle (lIdInst2, szExecuteString,
               strlen (szExecuteString) + 1, 0, (HSZ) NULL, CF_TEXT, 0L);

// Send the command over to the program manager.
         if (!DdeClientTransaction ((LPBYTE) hData, 0xFFFFFFFF,
               hConv, (HSZ) NULL, 0, XTYP_EXECUTE, 1000, &lResult)) {
            lResult = DdeGetLastError (lIdInst2);
         }/*endIf*/
      }/*endFor*/

// Release the memory allocated for path and name retrieval.
      VirtualFree (szProgName, MAX_PATH * 2, MEM_DECOMMIT);

// Release the command line memory.
      VirtualFree (szExecuteString, MAX_PATH * 4, MEM_DECOMMIT);

// Disoconnect the DDEML Conversation
      DdeDisconnect (hConv);

// Release the memory allocate for the list selections.
      VirtualFree (lpSelection, lSelCount * sizeof (int), MEM_DECOMMIT);
   }/*endIf*/

// Clear the selection in the lists.

   SendMessage (hwndFileList, LB_SETSEL, (WPARAM) FALSE, (LPARAM) -1);
   SendMessage (hwndFileList2, LB_SETSEL, (WPARAM) FALSE, (LPARAM) -1);
   EnableWindow (hwndAddAll, FALSE);

// Uninitialize the local conversation.
   DdeUninitialize (lIdInst2);

   if (hwndDialog) {
      PostMessage (ghwndMain, WM_USER_CLOSE_DIALOG, 0, 0L);
   }/*endIf*/
   lIdInst2 = 0L;
   if (fBatch) {
      PostMessage (ghwndMain, WM_COMMAND, (WPARAM)
            (WPARAM) MAKELONG (ID_EXITBUTTON, BN_CLICKED),
            (LPARAM) hwndExitButton);
   }/*endIf*/
   return (TRUE);
}/* end AddProgramItems */

HWND   hwndBar;
RECT   rc;
double iStep;
HBRUSH hBrush;

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

   UpdateProgressBar

   Function to update the progress bar in the dialog while items are
   added to the Program Manager.

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

void UpdateProgressBar (int iIndex, int iCount) {
   HDC  hdc;

   if (!fFirst) {
      hwndBar = GetDlgItem (hwndDialog, IDL_PROGRESSBAR);
      if (hwndBar) {
         GetClientRect (hwndBar, &rc);
         fFirst = TRUE;
      } else {
         return;
      }/*endIf*/
      iStep = (double) rc.right / (double) iCount;
      hBrush = GetStockObject (BLACK_BRUSH);
   }/*endIf*/
   hdc = GetDC (hwndBar);
   rc.right = (int) (iStep * (double) iIndex);
   FillRect (hdc, &rc, hBrush);
   ReleaseDC (hwndBar, hdc);

   PostMessage (hwndStatus, WM_USER_UPDATE_STATUS, iIndex, ID_DDEML_ADD);
}/* end UpdateProgressBar */


DDEMO.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*****************************************************************************\
*
* DDEMO.C
*
* This file implements a simple DDEML sample application that demonstrates
* some of the ways the DDEML APIs can be used.
*
* Each instance of this application becomes both a DDE client and a DDE
* server with any other instances of this application that are found.
*
* Since it assumes it is talking to itself, this program takes some liberties
* to simplify things.  For instance, this application does not support the
* standard SysTopic topic and does not use any standard formats.
*
* The basic concepts this application will show you are:
*
*   How to use lists of conversations properly
*   How to handle links
*   How to handle simple asynchronous transactions
*   How to use your own custom formats
*
\*****************************************************************************/
#include <windows.h>
#include <ddeml.h>
//#ifdef UNICODE
//#include <wchar.h>
//#endif // UNICODE
#include <stdlib.h>
#include <string.h>

#ifdef UNICODE
#define STRICMP wcsicmp
#define ITOA(c, sz, b) (itoa(sizeof(szA), szA, b), mbstowcs(sz, szA, b), sz)
#else
#define STRICMP stricmp
#define ITOA itoa
#endif  // UNICODE

HDDEDATA CALLBACK DdeCallback(WORD wType, WORD wFmt, HCONV hConv, HSZ hszTopic,
        HSZ hszItem, HDDEDATA hData, DWORD lData1, DWORD lData2);
VOID PaintDemo(HWND hwnd);
LONG  APIENTRY MainWndProc(HWND hwnd, UINT message, WPARAM wParam,
        LONG lParam);
VOID BroadcastTransaction(PBYTE pSrc,DWORD cbData,UINT fmt,UINT xtyp);

/*
 * Define this value to limit how fast data changes.  If we just let data
 * change as fast a possible, we might bog down the system with DDE
 * messages.
 */
#define BASE_TIMEOUT 100

BOOL        fActive;                    // indicates data is changing
DWORD       idInst = 0;                 // our DDEML instance object
HANDLE      hInst;                      // our instance/module handle
HCONVLIST   hConvList = 0;              // the list of all convs we have open
HSZ         hszAppName = 0;             // the generic hsz for everything
HWND        hwndMain;                   // our main window handle
TCHAR       szT[20];                    // static buffer for painting
#ifdef UNICODE
CHAR        szA[20];                    // static buffer for UNICODE conversion
TCHAR       szTitle[] = TEXT("DDEmo (U)");
#else
TCHAR       szTitle[] = TEXT("DDEmo");
#endif
TCHAR       szApp[] = TEXT("DDEmo");    // DDE service name
TCHAR       szPause[] = TEXT("PAUSE");  // DDE Execute command
TCHAR       szResume[] = TEXT("RESUME");// DDE Execute command
UINT        OurFormat;                  // our custom registered format
int         InCount = 0;                // static buffer to hold incomming data
int         cConvs = 0;                 // number of active conversations
int         count = 0;                  // our data
int         cyText, cxText, cyTitle;    // sizes for painting

int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
INT nCmdShow)
{
    MSG msg;
    WNDCLASS  wc;
    TEXTMETRIC metrics;
    HDC hdc;

    wc.style = 0;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = szTitle;

    if (!RegisterClass(&wc))
        return(FALSE);

    /*
     * Here we tell DDEML what we will be doing.
     *
     * 1) We let it know our callback proc address - MakeProcInstance
     *      is called just to be more portable.
     * 2) Filter-inits - don't accept any WM_DDE_INITIATE messages for
     *      anything but our registered service name.
     * 3) Don't bother to notify us of confirmed connections
     * 4) Don't allow connections with ourselves.
     * 5) Don't bother us with XTYP_POKE transactions.
     */
    if (DdeInitialize(&idInst,
            (PFNCALLBACK)MakeProcInstance((FARPROC)DdeCallback, hInstance),
            APPCMD_FILTERINITS |
            CBF_SKIP_CONNECT_CONFIRMS |
            CBF_FAIL_SELFCONNECTIONS |
            CBF_FAIL_POKES,
            0))
        return(FALSE);

    hInst = hInstance;
    hwndMain = CreateWindow(
        szTitle,
        szTitle,
        WS_CAPTION | WS_BORDER | WS_SYSMENU,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        0,
        0,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwndMain) {
        DdeUninitialize(idInst);
        return(FALSE);
    }

    hdc = GetDC(hwndMain);
    GetTextMetrics(hdc, &metrics);
    cyText = metrics.tmHeight + metrics.tmExternalLeading;
    cxText = metrics.tmMaxCharWidth * 8;
    cyTitle = GetSystemMetrics(SM_CYCAPTION);
    ReleaseDC(hwndMain, hdc);

     /*
     * Initialize all our string handles for lookups later
     */
    hszAppName = DdeCreateStringHandle(idInst, szApp, 0);
    /*
     * Register our formats
     */
    OurFormat = RegisterClipboardFormat(szApp);
    /*
     * Register our service -
     *  This will cause DDEML to notify DDEML clients about the existance
     *  of a new DDE service.
     */
    DdeNameService(idInst, hszAppName, 0, DNS_REGISTER);
    /*
     * Connect to any other instances of ourselves that may already be
     * running.
     */
    hConvList = DdeConnectList(idInst, hszAppName, hszAppName, hConvList, NULL);
    BroadcastTransaction(NULL, 0, OurFormat, XTYP_ADVSTART);

    SetWindowPos(hwndMain, 0, 0, 0, cxText,
                (cyText * (cConvs + 1)) + cyTitle, SWP_NOMOVE | SWP_NOZORDER);
    ShowWindow(hwndMain, nCmdShow);
    UpdateWindow(hwndMain);

    while (GetMessage(&msg, 0, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    DestroyWindow(hwndMain);
    UnregisterClass(szTitle, hInstance);
    return(FALSE);
}

/*
 * BroadcastTransaction
 *
 * Does the specified transaction on all conversations in hConvList
 */
VOID BroadcastTransaction(
PBYTE pSrc,
DWORD cbData,
UINT fmt,
UINT xtyp)
{
    HCONV hConv;
    DWORD dwResult;
    int cConvsOrg;

    cConvsOrg = cConvs;
    cConvs = 0;
    if (hConvList) {
        /*
         * Enumerate all the conversations within this list - note that
         * DDEML will only return active conversations.  Inactive conversations
         * are automatically removed.
         */
        hConv = DdeQueryNextServer(hConvList, 0);
        while (hConv) {
            /*
             * Count the active conversations while we're at it.
             */
            cConvs++;
            /*
             * Spawn an asynchronous transaction - this was chosen because
             * we have not particular action if an error ocurrs so we just
             * don't care too much about the results - this technique will
             * NOT do for XTYP_REQUEST transactions though.
             */
            if (DdeClientTransaction(pSrc, cbData, hConv, hszAppName, fmt,
                    xtyp, TIMEOUT_ASYNC, &dwResult)) {
                /*
                 * We immediately abandon the transaction so we don't get
                 * a bothersome XTYP_XACT_COMPLETE callback which we don't
                 * care about.
                 */
                DdeAbandonTransaction(idInst, hConv, dwResult);
            }

            hConv = DdeQueryNextServer(hConvList, hConv);
        }
    }
    if (cConvs != cConvsOrg) {
        /*
         * Oh, the number of active conversations has changed.  Time to
         * repaint!
         */
        InvalidateRect(hwndMain, NULL, TRUE);
    }
}

/*
 * MyProcessKey
 *
 * We demonstrate the robustness of NT here by forcing a GP anytime the
 * 'B' key is pressed while this window has the focus.  NT should properly
 * fake termination to all other apps connected to us.
 */
VOID MyProcessKey(
TCHAR tchCode,
LONG lKeyData)
{
    switch (tchCode) {
    case TEXT('B'):
    case TEXT('b'):
        *((PBYTE)(-1)) = 0;    // Cause GP fault!
        break;
    }
}

LONG  APIENTRY MainWndProc(
HWND hwnd,
UINT message,
WPARAM wParam,
LONG lParam)
{
    RECT rc;

    switch (message) {
    case WM_CREATE:
        /*
         * initially we are inactive - this reduces some of the message
         * traffic while we are initializing - but we could start active fine.
         */
        fActive = FALSE;
        break;

    case WM_RBUTTONDOWN:
        if (GetKeyState(VK_CONTROL) & 0x8000) {
            /*
             * A CTRL R_BUTTON click will cause ALL instances of this app
             * to become inactive.
             */
            BroadcastTransaction((PBYTE)szPause, sizeof(szPause), 0, XTYP_EXECUTE);
            MessageBeep(0);
        }
        /*
         * A R_BUTTON click makes us inactive.  Repaint to show state change.
         * We do a synchronous update in case there is too much DDE message
         * activity to allow the WM_PAINT messages through.  Remember DDE
         * messages have priority over others!
         */
        KillTimer(hwndMain, 1);
        fActive = FALSE;
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        break;

    case WM_LBUTTONDOWN:
        if (GetKeyState(VK_CONTROL) & 0x8000) {
            /*
             * A CTRL L_BUTTON click will cause ALL instances of this app
             * to become active.
             */
            BroadcastTransaction((PBYTE)szResume, sizeof(szResume), 0, XTYP_EXECUTE);
            MessageBeep(0);
        }
        /*
         * An L_BUTTON click makes us active.  Repaint to show state change.
         */
        SetTimer(hwndMain, 1, BASE_TIMEOUT + (rand() & 0xff), NULL);
        fActive = TRUE;
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        break;

    case WM_CHAR:
        MyProcessKey((TCHAR)wParam, lParam);
        break;

    case WM_TIMER:
        /*
         * We use timers for simplicity.  On Win3.1 we could run out of
         * timers easily but we don't have this worry on NT.
         *
         * Each tick, we increment our data and call DdePostAdvise() to
         * update any links there may be on this data.  DDEML makes link
         * updates on specific items quite easy.
         */
        count++;
        DdePostAdvise(idInst, hszAppName, hszAppName);
        /*
         * Invalidate the part of ourselves that shows our data and
         * synchronously update it in case DDE message activity is blocking
         * paints.
         */
        SetRect(&rc, 0, 0, cxText, cyText);
        InvalidateRect(hwndMain, &rc, TRUE);
        UpdateWindow(hwndMain);
        break;

    case WM_PAINT:
        PaintDemo(hwnd);
        break;

    case WM_CLOSE:
        KillTimer(hwnd, 1);
        /*
         * We do DDE cleanup here.  It is best to do DDE cleanup while
         * still in the message loop to allow DDEML to recieve messages
         * while shutting down.
         */
        DdeDisconnectList(hConvList);
        DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
        DdeFreeStringHandle(idInst, hszAppName);
        DdeUninitialize(idInst);
        PostQuitMessage(0);
        break;

    default:
        return (DefWindowProc(hwnd, message, wParam, lParam));
    }
    return(0);
}

VOID PaintDemo(
HWND hwnd)
{
    PAINTSTRUCT ps;
    RECT rc;
    HCONV hConv;
    CONVINFO ci;
    int cConvsOrg = cConvs;

    BeginPaint(hwnd, &ps);
    /*
     * Draw our data on top - Black for active, Grey for inactive.
     */
    SetRect(&rc, 0, 0, cxText, cyText);
    SetBkMode(ps.hdc, TRANSPARENT);
    SetTextColor(ps.hdc, 0x00FFFFFF);   // white text
    FillRect(ps.hdc, &rc, GetStockObject(fActive ? BLACK_BRUSH : GRAY_BRUSH));
    DrawText(ps.hdc, ITOA(count, szT, 10), -1, &rc, DT_CENTER | DT_VCENTER);

    /*
     * Now draw the most recently recieved data from each server we are
     * connected to.
     */
    if (hConvList) {
        OffsetRect(&rc, 0, cyText);
        SetTextColor(ps.hdc, 0);    // draw black text
        cConvs = 0;
        hConv = DdeQueryNextServer(hConvList, 0);
        while (hConv) {
            cConvs++;
            /*
             * count how many conversations are active while we're at it.
             */
            ci.cb = sizeof(CONVINFO);
            DdeQueryConvInfo(hConv, QID_SYNC, &ci);
            FillRect(ps.hdc, &rc, GetStockObject(WHITE_BRUSH));  // white bkgnd
            DrawText(ps.hdc, ITOA(ci.hUser, szT, 10), -1, &rc,
                    DT_CENTER | DT_VCENTER);
            OffsetRect(&rc, 0, cyText);
            hConv = DdeQueryNextServer(hConvList, hConv);
        }
    }
    EndPaint(hwnd, &ps);
    if (cConvsOrg != cConvs) {
        /*
         * The number of active conversations changed!  Resize to fit.
         */
        SetWindowPos(hwndMain, 0, 0, 0, cxText,
                (cyText * (cConvs + 1)) + cyTitle,
                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
    }
}

/*
 * This is the main DDEML callback proc.  It handles all interaction with
 * DDEML that is DDEML originated.
 */
HDDEDATA CALLBACK DdeCallback(
WORD wType,
WORD wFmt,
HCONV hConv,
HSZ hszTopic,
HSZ hszItem,
HDDEDATA hData,
DWORD lData1,
DWORD lData2)
{
    LPTSTR pszExec;

    switch (wType) {
    case XTYP_CONNECT:
        /*
         * Only allow connections to us.  We can always return TRUE because
         * the CBF_FILTERINITS bit given to DdeInitialize() told DDEML to
         * never bother us with connections to any service names other than
         * what we have registered.
         *
         * Note that we do not handle the XTYP_WILD_CONNECT transaction.
         * This means that no wild-card initiates to us will work.
         */
        return((HDDEDATA)TRUE);

    case XTYP_ADVREQ:
    case XTYP_REQUEST:
        /*
         * These two transactions are the only ones that require us to
         * render our data.  By using a custom format, we don't have to
         * convert our count to text form to support CF_TEXT.
         */
        return(DdeCreateDataHandle(idInst, (PBYTE)&count, sizeof(count), 0,
                hszAppName, OurFormat, 0));

    case XTYP_ADVSTART:
        /*
         * Only allow links to our Item in our format.
         */
        return(HDDEDATA) ((UINT)wFmt == OurFormat && hszItem == hszAppName);

    case XTYP_ADVDATA:
        /*
         * Data is comming in.  We don't bother with XTYP_POKE transactions,
         * but if we did, they would go here.  Since we only allow links
         * on our item and our format, we need not check these here.
         */
        if (DdeGetData(hData, (PBYTE)&InCount, sizeof(InCount), 0)) {
            DdeSetUserHandle(hConv, QID_SYNC, InCount);
        }
        /*
         * update ourselves to reflect the new incomming data.
         */
        InvalidateRect(hwndMain, NULL, TRUE);
        /*
         * This transaction requires a flag return value.  We could also
         * stick other status bits here if needed but its not recommended.
         */
        return((HDDEDATA)DDE_FACK);

    case XTYP_EXECUTE:
        /*
         * Another instance wants us to do something.  DdeAccessData()
         * makes parsing of execute strings easy.  Also note, that DDEML
         * will automatically give us the string in the right form
         * (UNICODE vs ASCII) depending on which form of DdeInitialize()
         * we called.
         */
        pszExec = (LPTSTR)DdeAccessData(hData, NULL);
        if (pszExec) {
            if (fActive && !STRICMP(szPause, pszExec)) {
                KillTimer(hwndMain, 1);
                fActive = FALSE;
                InvalidateRect(hwndMain, NULL, TRUE);
                UpdateWindow(hwndMain);
            } else if (!fActive && !STRICMP(szResume, pszExec)) {
                SetTimer(hwndMain, 1, BASE_TIMEOUT + (rand() & 0xff), NULL);
                fActive = TRUE;
                InvalidateRect(hwndMain, NULL, TRUE);
                UpdateWindow(hwndMain);
            }
            /*
             * The beep gives good feedback on how fast the execute was.
             */
            MessageBeep(0);
        }
        break;

    case XTYP_DISCONNECT:
        /*
         * Somebody went away, repaint so we update our cConvs count.
         */
        InvalidateRect(hwndMain, NULL, TRUE);
        break;

    case XTYP_REGISTER:
        /*
         * Since a new server just arrived, lets make sure our links are
         * up to date.  Note that only one link on a
         * conversation/topic/item/format set will work anyway so we don't
         * worry about duplicate links.
         *
         * Note also that we are using hszItem - which is the InstanceSpecific
         * name of the server that is registering.  This greatly reduces the
         * number of messages that go flying around.
         */
BroadcastTransaction( NULL, 0, OurFormat, XTYP_ADVSTOP );
        hConvList = DdeConnectList(idInst, hszItem, hszAppName, hConvList, NULL);
        BroadcastTransaction(NULL, 0, OurFormat, XTYP_ADVSTART);
        SetWindowPos(hwndMain, 0, 0, 0, cxText,
                (cyText * (cConvs + 1)) + cyTitle, SWP_NOMOVE | SWP_NOZORDER);
        UpdateWindow(hwndMain);
        return((HDDEDATA)TRUE);
    }
    return(0);
}


PHTEST.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include "phtest.h"
#include "proghelp.h"

LONG  APIENTRY MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LONG lParam);

DWORD GFlags;
DWORD type;
HANDLE hKey;

int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
INT nCmdShow)
{
    DialogBox(hInstance,
            MAKEINTRESOURCE(DID_PHTEST),
            NULL,
            (DLGPROC)MainWndProc);

    return(TRUE);
}

LONG  APIENTRY MainWndProc(
HWND hwnd,
UINT message,
WPARAM wParam,
LONG lParam)
{
    TCHAR szT[100];

    switch (message) {
    case WM_INITDIALOG:
        if (!ConnectToProgman()) {
            PostMessage(hwnd, WM_CLOSE, 0, 0);
            return(0);
        }
        return(IDEF_EXECTEXT);

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
            GetDlgItemText(hwnd, IDEF_EXECTEXT, szT, sizeof(szT));
            if (!ProgmanExecuteString(szT)) {
                MessageBeep(0);
            }
        }
        break;

    case WM_CLOSE:
        ShowWindow(hwnd, SW_HIDE);
        if (!DisconnectFromProgman()) {
            /*
             * Progman is not done yet - we need to wait around for the
             * transactions to complete before closing down so just
             * keep posting WM_CLOSE to ourselves to allow DDEML's DDE
             * messages to finish.
             */
            Sleep(100);
            PostMessage(hwnd, WM_CLOSE, 0, 0);
            return(0);
        };
        EndDialog(hwnd, 0);
        break;

    }
    return(0);
}


PHTEST.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define DID_PHTEST                  100
#define IDEF_EXECTEXT               101


PROGHELP.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*****************************************************************************\
* PROGHELP.C
*
* Main module to implement a simple DDEML service DLL that makes interaction
* with progman simple.
\*****************************************************************************/

#include <windows.h>
#include <ddeml.h>
#include <string.h>

typedef struct {
    HCONV hConv;
    DWORD idInst;
} THREADINFO;

DWORD TlsIndex;

/*
 * This is the default DDEML callback function for ProgHelp
 */
HDDEDATA CALLBACK ProgHelpDdeCallback(
UINT wType,
UINT wFmt,
HCONV hConv,
HSZ hsz1,
HSZ hsz2,
HDDEDATA hData,
DWORD dwData1,
DWORD dwData2)
{
    return(0);
}

/*
 * This is the DLL init routine called by the system at attach/detatch time.
 *
 * We allocate a Tls for each thread that calls this DLL so that their state
 * can be tracked independently.
 */
BOOLEAN APIENTRY DllMain(
IN PVOID hmod,
ULONG Reason,
IN PCONTEXT pctx OPTIONAL)
{
    THREADINFO *pti;
    UNREFERENCED_PARAMETER(hmod);
    UNREFERENCED_PARAMETER(pctx);

    switch (Reason) {
    case DLL_PROCESS_ATTACH:
        TlsIndex = TlsAlloc();

    case DLL_THREAD_ATTACH:
        pti = (THREADINFO *)LocalAlloc(LPTR, sizeof(THREADINFO));
        if (!pti) {
            return(FALSE);
        }
        TlsSetValue(TlsIndex, pti);
        break;

    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        pti = TlsGetValue(TlsIndex);
        LocalFree(pti);
        if (Reason == DLL_PROCESS_DETACH) {
            TlsFree(TlsIndex);
        }
        break;
    }
    return TRUE;
}

/*
 * An application must call this API first to establish a conversation
 * with progman.  DDEML initialization is done automatically if needed.
 */
BOOL ConnectToProgmanA()
{
    THREADINFO *pti;
    HSZ hszProgman;

    pti = TlsGetValue(TlsIndex);

    if (!pti->idInst) {
        if (DdeInitializeA(&(pti->idInst), ProgHelpDdeCallback,
                APPCMD_CLIENTONLY | CBF_SKIP_ALLNOTIFICATIONS,
                0L) != DMLERR_NO_ERROR) {
            return(FALSE);
        }
    }

    if (pti->hConv == 0) {
        hszProgman = DdeCreateStringHandleA(pti->idInst, "Progman", 0);
        pti->hConv = DdeConnect(pti->idInst, hszProgman, hszProgman, NULL);
    }
    return(pti->hConv != 0);
}

BOOL ConnectToProgmanW()
{
    THREADINFO *pti;
    HSZ hszProgman;

    pti = TlsGetValue(TlsIndex);

    if (!pti->idInst) {
        if (DdeInitializeW(&(pti->idInst), ProgHelpDdeCallback,
                APPCMD_CLIENTONLY | CBF_SKIP_ALLNOTIFICATIONS,
                0L) != DMLERR_NO_ERROR) {
            return(FALSE);
        }
    }

    if (pti->hConv == 0) {
        hszProgman = DdeCreateStringHandleA(pti->idInst, "Progman", 0);
        pti->hConv = DdeConnect(pti->idInst, hszProgman, hszProgman, NULL);
    }
    return(pti->hConv != 0);
}

/*
 * This API should be called when operations with progman are complete.
 * We shutdown DDEML here as well so we don't have to deal with it
 * at DLL detatch time.
 */
BOOL DisconnectFromProgman()
{
    THREADINFO *pti;

    pti = TlsGetValue(TlsIndex);

    if (!pti->idInst) {
        return(TRUE);
    }

    if (pti->hConv) {
        DdeDisconnect(pti->hConv);
        pti->hConv = 0;
    }

    DdeUninitialize(pti->idInst);
    pti->idInst = 0;
    return(TRUE);
}

/*
 * This API sends an ASCII execute string to progman.  It immediately returns
 * and the results of the execute are not known.  Calling applications should
 * be aware that these transactions are done asynchronously.  This allows
 * this API to work under modal-loop conditions.  DisconnectFromProgman()
 * will not succeed until all transactions are complete.
 */
BOOL ProgmanExecuteStringA(
LPSTR pszExec)
{
    THREADINFO *pti;
    DWORD dwResult;

    pti = TlsGetValue(TlsIndex);

    if (!pti->idInst || !pti->hConv) {
        return(FALSE);
    }
    if (!DdeClientTransaction((PBYTE)pszExec, strlen(pszExec) + 1,
            pti->hConv, 0, 0, XTYP_EXECUTE, TIMEOUT_ASYNC, &dwResult)) {
        return(FALSE);
    }
    return(TRUE);
}

BOOL ProgmanExecuteStringW(
LPWSTR pszExec)
{
    THREADINFO *pti;
    DWORD dwResult;

    pti = TlsGetValue(TlsIndex);

    if (!pti->idInst || !pti->hConv) {
        return(FALSE);
    }
    if (!DdeClientTransaction((PBYTE)pszExec,
            (wcslen(pszExec) + 1)  * sizeof(WCHAR),
            pti->hConv, 0, 0, XTYP_EXECUTE, TIMEOUT_ASYNC, &dwResult)) {
        return(FALSE);
    }
    return(TRUE);
}


PROGHELP.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

BOOL DisconnectFromProgman(VOID);
BOOL ConnectToProgmanW(VOID);
BOOL ConnectToProgmanA(VOID);
BOOL ProgmanExecuteStringW(LPWSTR pszExec);
BOOL ProgmanExecuteStringA(LPSTR pszExec);

#ifdef UNICODE
#define ConnectToProgman ConnectToProgmanW
#define ProgmanExecuteString ProgmanExecuteStringW
#else
#define ConnectToProgman ConnectToProgmanA
#define ProgmanExecuteString ProgmanExecuteStringA
#endif


DDE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*
 * This module serves to demonstrate one way a sophisticated DDE server
 * that uses enumerable topics and items might be implemented.  It takes
 * full advantage of appowned data handles (when fAppowned is set) to
 * minimize the need for repeated rendering of data when shared with
 * multiple clients.
 *
 * The server supports full system topic information plus help and non
 * system topic item enumeration for the benefit of browsing clients
 * that are wondering what's around.
 *
 * This server can be made secure by altering the conversation context
 * filter.
 *
 * This server can appear to support alternate codepages and languages
 * by altering the conversation context filter.   On Windows this is
 * pretty much moot since there is not yet a clearly defined way of
 * doing international communication and because the atom manager restricts
 * what topic and item strings can be used on the system.
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "server.h"
#include "huge.h"

/*
 * This function verifies that the incomming conversation context fits the
 * server's context filter's requirements.
 */
BOOL ValidateContext(
PCONVCONTEXT pCC)
{
    // make sure our CCFilter allows it...mock security, language support
    // old DDE app client case...pCC == NULL
    if (pCC == NULL &&
            CCFilter.dwSecurity == 0 &&      // were nonsecure
            CCFilter.iCodePage == CP_WINANSI && // were normal cp
            CCFilter.qos.ImpersonationLevel == SecurityImpersonation
            ) {
        return(TRUE);
    }

    if (pCC &&
            pCC->wFlags == CCFilter.wFlags && // no special flags needed
            pCC->iCodePage == CCFilter.iCodePage && // codepages match
            pCC->dwSecurity == CCFilter.dwSecurity && // security passes
            pCC->qos.ImpersonationLevel >= CCFilter.qos.ImpersonationLevel) {
        // dont care about language and country.
        return(TRUE);
    }
    return(FALSE);  // disallow no match
}

/***************************** Public  Function ****************************\
*
* This function is called by the DDE manager DLL and passes control onto
* the apropriate function pointed to by the global topic and item arrays.
* It handles all DDE interaction generated by external events.
*
\***************************************************************************/
HDDEDATA CALLBACK DdeCallback(
UINT wType,
UINT wFmt,
HCONV hConv,
HSZ hszTopic,
HSZ hszItem,
HDDEDATA hData,
DWORD lData1,
DWORD lData2)
{
    WORD i, j;
    register ITEMLIST *pItemList;
    WORD cItems, iFmt;
    HDDEDATA hDataRet;

    /*
     * Block this callback if its blockable and we are supposed to.
     */
    if (fBlockNextCB && !(wType & XTYPF_NOBLOCK)) {
        fBlockNextCB = FALSE;
        fAllEnabled = FALSE;
        return(CBR_BLOCK);
    }

    /*
     * Block this callback if its associated with a conversation and we
     * are supposed to.
     */
    if (fTermNextCB && hConv) {
        fTermNextCB = FALSE;
        DdeDisconnect(hConv);
        wType = XTYP_DISCONNECT;
    }

    /*
     * Keep a count of connections
     */
    if (wType == XTYP_CONNECT_CONFIRM) {
        cServers++;
        InvalidateRect(hwndServer, &rcConnCount, TRUE);
        return(0);
    }
    if (wType == XTYP_DISCONNECT) {
        cServers--;
        InvalidateRect(hwndServer, &rcConnCount, TRUE);
        return(0);
    }

    /*
     * only allow transactions on the formats we support if they have a format.
     */
    if (wFmt) {
        for (iFmt = 0; iFmt < CFORMATS; iFmt++) {
            if ((ATOM)wFmt == aFormats[iFmt].atom)
                break;
        }
        if (iFmt == CFORMATS)
            return(0);          // illegal format - ignore now.
    }

    /*
     * Executes are allowed only on the system topic.  This is a general
     * convention, not a requirement.
     *
     * Any executes received result in the execute text being shown in
     * the server client area.  No real action is taken.
     */
    if (wType == XTYP_EXECUTE) {
        if (hszTopic == topicList[0].hszTopic) {    // must be on system topic
            // Format is assumed to be CF_TEXT.
            DdeGetData(hData, (LPBYTE)szExec, MAX_EXEC, 0);
            szExec[MAX_EXEC - 1] = TEXT('\0');
            InvalidateRect(hwndServer, &rcExec, TRUE);
            hDataRet = (HDDEDATA)TRUE;
            goto ReturnSpot;
        }
        pszComment = TEXT("Execute received on non-system topic - ignored");
        InvalidateRect(hwndServer, &rcComment, TRUE);
        return(0);
    }

    /*
     * Process wild initiates here
     */
    if (wType == XTYP_WILDCONNECT) {
        HSZ ahsz[(CTOPICS + 1) * 2];
        /*
         * He wants a hsz list of all our available app/topic pairs
         * that conform to hszTopic and hszItem(App).
         */

        if (!ValidateContext((PCONVCONTEXT)lData1)) {
            return(FALSE);
        }

        if (hszItem != hszAppName && hszItem != 0) {
            // we only support the hszAppName service
            return(0);
        }

        // scan the topic table and create hsz pairs
        j = 0;
        for (i = 0; i < CTOPICS; i++) {
            if (hszTopic == 0 || hszTopic == topicList[i].hszTopic) {
                ahsz[j++] = hszAppName;
                ahsz[j++] = topicList[i].hszTopic;
            }
        }

        // cap off the list with 0s
        ahsz[j++] = ahsz[j++] = 0L;

        // send it back
        return(DdeCreateDataHandle(idInst, (LPBYTE)&ahsz[0], sizeof(HSZ) * j, 0L, 0, wFmt, 0));
    }

    /*
     * Check our hsz tables and send to the apropriate proc. to process.
     * We use DdeCmpStringHandles() which is the portable case-insensitive
     * method of comparing string handles.  (this is a macro on windows so
     * there is no real speed hit.)  On WINDOWS, HSZs are case-insensitive
     * anyway, but this may not be the case on other platforms.
     */
    for (i = 0; i < CTOPICS; i++) {
        if (DdeCmpStringHandles(topicList[i].hszTopic, hszTopic) == 0) {

            /*
             * connections must be on a topic we support.
             */
            if (wType == XTYP_CONNECT) {
                return((HDDEDATA)ValidateContext((PCONVCONTEXT)lData1));
            }

            pItemList = topicList[i].pItemList;
            cItems = topicList[i].cItems;
            for (j = 0; j < cItems; j++) {
                if (DdeCmpStringHandles(pItemList[j].hszItem, hszItem) == 0) {
                    XFERINFO xi;

                    /*
                     * If Win32s - security isn't supported
                     */
                    if(GetVersion() & 0x80000000) {
                        /*
                         * Make call to worker function here...
                         */
                        xi.wType = wType;
                        xi.wFmt = wFmt;
                        xi.hConv = hConv;
                        xi.hszTopic = hszTopic;
                        xi.hszItem = hszItem;
                        xi.hData = hData;
                        xi.lData1 = lData1;
                        xi.lData2 = lData2;
                        hDataRet = (*pItemList[j].npfnCallback)(&xi, iFmt);
                     } else {
                        /*
                         * Windows NT - impersonate client here
                         */
                        if (DdeImpersonateClient(hConv)) {
                            /*
                             * Make call to worker function here...
                             */
                            xi.wType = wType;
                            xi.wFmt = wFmt;
                            xi.hConv = hConv;
                            xi.hszTopic = hszTopic;
                            xi.hszItem = hszItem;
                            xi.hData = hData;
                            xi.lData1 = lData1;
                            xi.lData2 = lData2;
                            hDataRet = (*pItemList[j].npfnCallback)(&xi, iFmt);
                            RevertToSelf();
                        } else {
                            pszComment = TEXT("Impersonation failed.");
                            InvalidateRect(hwndServer, &rcComment, TRUE);
                            hDataRet = 0;
                        }
                     }

ReturnSpot:
                    /*
                     * The table functions return a boolean or data.
                     * It gets translated here.
                     */
                    switch (wType & XCLASS_MASK) {
                    case XCLASS_DATA:
                        return(hDataRet);
                        break;
                    case XCLASS_FLAGS:
                        return(HDDEDATA)(hDataRet ? DDE_FACK : DDE_FNOTPROCESSED);
                        break;
                    case XCLASS_BOOL:
                        return((HDDEDATA)TRUE);
                    default: // XCLASS_NOTIFICATION
                        return(0);
                        break;
                    }
                    break;
                }
            }
            break;
        }
    }

    /*
     * anything else fails - DDEML is designed so that a 0 return is ALWAYS ok.
     */
    return(0);
}

/***************************** Private Function ****************************\
* This passes out a standard tab-delimited list of topic names for this
* application.
*
* This support is required for other apps to be able to
* find out about us.  This kind of support should be in every DDE
* application.
*
\***************************************************************************/
HDDEDATA TopicListXfer(
PXFERINFO pXferInfo,
WORD iFmt)
{
    WORD cbAlloc, i;
    LPTSTR pszTopicList;
    HDDEDATA hData;

    if (pXferInfo->wType == XTYP_ADVSTART)
        return((HDDEDATA)TRUE);

    if (pXferInfo->wType != XTYP_REQUEST &&
            pXferInfo->wType != XTYP_ADVREQ)
        return(0);
    /*
     * construct the list of topics we have
     */
    cbAlloc = 0;
    for (i = 0; i < CTOPICS; i++)
        cbAlloc += (_tcslen(topicList[i].pszTopic) + 1) * sizeof(TCHAR);  // 1 for tab

    // allocate a data handle big enough for the list.
    hData = DdeCreateDataHandle(idInst, NULL, 0, cbAlloc, pXferInfo->hszItem,
            pXferInfo->wFmt, 0);
    pszTopicList = (LPTSTR)DdeAccessData(hData, NULL);
    if (pszTopicList) {
        for (i = 0; i < CTOPICS; i++) {
            _tcscpy(pszTopicList, topicList[i].pszTopic);
            pszTopicList += _tcslen(topicList[i].pszTopic);
            *pszTopicList++ = TEXT('\t');
        }
        *--pszTopicList = TEXT('\0');
        DdeUnaccessData(hData);
        return(hData);
    }
    return(0);
}

/***************************** Private Function ****************************\
* This passes out a standard tab-delimited list of item names for the
* specified topic.
*
* This support is required for other apps to be able to
* find out about us.  This kind of support should be in every DDE
* application.
*
\***************************************************************************/
HDDEDATA ItemListXfer(
PXFERINFO pXferInfo,
WORD iFmt)
{
    WORD cbAlloc, i, iItem, cItems;
    ITEMLIST *pItemList = 0;
    LPTSTR pszItemList;
    HDDEDATA hData;

    if (pXferInfo->wType == XTYP_ADVSTART)
        return((HDDEDATA)TRUE);

    if (pXferInfo->wType != XTYP_REQUEST &&
                pXferInfo->wType != XTYP_ADVREQ)
        return(0);
    /*
     * construct the list of items we support for this topic - this supports
     * more than the minimum standard which would support SysItems only on
     * the system topic.
     */

    // locate the requested topic item table
    for (i = 0; i < CTOPICS; i++) {
        if (pXferInfo->hszTopic == topicList[i].hszTopic) {
            pItemList = topicList[i].pItemList;
            cItems = topicList[i].cItems;
            break;
        }
    }

    if (!pItemList)
        return(0);  // item not found

    cbAlloc = 0;
    for (iItem = 0; iItem < cItems; iItem++)
        cbAlloc += (_tcslen(pItemList[iItem].pszItem) + 1) * sizeof(TCHAR); // 1 for tab

    // allocate a data handle big enough for the list.
    hData = DdeCreateDataHandle(idInst, NULL, 0, cbAlloc, pXferInfo->hszItem,
            pXferInfo->wFmt, 0);
    pszItemList = (LPTSTR)DdeAccessData(hData, NULL);
    if (pszItemList) {
        for (i = 0; i < cItems; i++) {
            _tcscpy(pszItemList, pItemList[i].pszItem);
            pszItemList += _tcslen(pItemList[i].pszItem);
            *pszItemList++ = TEXT('\t');
        }
        *--pszItemList = TEXT('\0');
        DdeUnaccessData(hData);
        return(hData);
    }
    return(0);
}

/***************************** Private Function ****************************\
* Gives out a 0 terminated array of dde format numbers supported by this app.
*
* This support is required for other apps to be able to
* find out about us.  This kind of support should be in every DDE
* application.
*
\***************************************************************************/
HDDEDATA sysFormatsXfer(
PXFERINFO pXferInfo,
WORD iFmt)
{
    INT i, cb;
    LPTSTR psz, pszT;
    HDDEDATA hData;

    if (pXferInfo->wType == XTYP_ADVSTART)
        return((HDDEDATA)TRUE);

    if (pXferInfo->wType != XTYP_REQUEST &&
            pXferInfo->wType != XTYP_ADVREQ)
        return(0);

    for (i = 0, cb = 0; i < CFORMATS; i++)
        cb += (_tcslen(aFormats[i].sz) + 1) * sizeof(TCHAR);

    hData = DdeCreateDataHandle(idInst, NULL, (DWORD)cb,
            0L, pXferInfo->hszItem, pXferInfo->wFmt, 0);
    psz = pszT = (LPTSTR)DdeAccessData(hData, NULL);
    for (i = 0; i < CFORMATS; i++) {
        _tcscpy(pszT, aFormats[i].sz);
        pszT += _tcslen(pszT);
        *pszT++ = TEXT('\t');
    }
    *(--pszT) = TEXT('\0');
    DdeUnaccessData(hData);
    return(hData);
}

/*
 * This is a runaway item.  Each time it is requested, it changes.
 * pokes just make it change again.
 */
HDDEDATA TestRandomXfer(
PXFERINFO pXferInfo,
WORD iFmt)
{
    TCHAR szT[10];   // SS==DS!
    LPTSTR pszData;
    HDDEDATA hData;
    WORD i;

    switch (pXferInfo->wType) {
    case XTYP_POKE:
        // we expect an ascii number to replace the current seed.
        pszComment = TEXT("Rand poke received.");
        InvalidateRect(hwndServer, &rcComment, TRUE);
        InvalidateRect(hwndServer, &rcRand, TRUE);
        if (DdeGetData(pXferInfo->hData, (PBYTE)szT, 10, 0)) {
            szT[9] = TEXT('\0');  // just incase we overran.
            _stscanf(szT, TEXT("%d"), &seed);
            for (i = 0; i < CFORMATS; i++) {
                if (hDataRand[i])
                    DdeFreeDataHandle(hDataRand[i]);
                hDataRand[i] = 0;
            }
            DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
            return((HDDEDATA)1);
        }
        break;

    case XTYP_REQUEST:
        pszComment = TEXT("Rand data requested.");
        InvalidateRect(hwndServer, &rcComment, TRUE);
    case XTYP_ADVREQ:
        Delay(RenderDelay, FALSE);
        if (!hDataRand[iFmt]) {
            hDataRand[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10,
                    pXferInfo->hszItem, (UINT)pXferInfo->wFmt,
                    fAppowned ? HDATA_APPOWNED : 0);
            if (pszData = (LPTSTR)DdeAccessData(hDataRand[iFmt], NULL)) {
                wsprintf(pszData, TEXT("%d"), seed);
                DdeUnaccessData(hDataRand[iFmt]);
            }
        }
        hData = hDataRand[iFmt];
        if (!fAppowned)
            hDataRand[iFmt] = 0;
        return(hData);
        break;

    case XTYP_ADVSTART:
        return((HDDEDATA)1);
    }
    return(0);
}

/*
 * This is a runaway item.  Each time it is requested, it changes.
 * pokes just make it change again.
 */
HDDEDATA TestCountXfer(
PXFERINFO pXferInfo,
WORD iFmt)
{
    TCHAR szT[16];   // SS==DS!
    LPTSTR pszData;
    HDDEDATA hData;
    WORD i;

    switch (pXferInfo->wType) {
    case XTYP_POKE:
        // we expect an ascii number to replace the current count.
        pszComment = TEXT("Count poke received");
        InvalidateRect(hwndServer, &rcComment, TRUE);
        InvalidateRect(hwndServer, &rcCount, TRUE);
        if (DdeGetData(pXferInfo->hData, (PBYTE)szT, 10, 0)) {
            szT[9] = TEXT('\0');  // just incase we overran.
            _stscanf(szT, TEXT("%ld"), &count);
            for (i = 0; i < CFORMATS; i++) {
                if (hDataCount[i])
                    DdeFreeDataHandle(hDataCount[i]);
                hDataCount[i] = 0;
            }
            DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
            return((HDDEDATA)1);
        }
        break;

    case XTYP_REQUEST:
        pszComment = TEXT("Count data requested.");
        InvalidateRect(hwndServer, &rcComment, TRUE);
    case XTYP_ADVREQ:
        Delay(RenderDelay, FALSE);
        if (!hDataCount[iFmt]) {
            hDataCount[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10, pXferInfo->hszItem,
                    pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
            if (pszData = (LPTSTR)DdeAccessData(hDataCount[iFmt], NULL)) {
                wsprintf(pszData, TEXT("%ld"), count);
                DdeUnaccessData(hDataCount[iFmt]);
            }
        }
        hData = hDataCount[iFmt];
        if (!fAppowned)
            hDataCount[iFmt] = 0;
        return(hData);
        break;

    case XTYP_ADVSTART:
        return((HDDEDATA)1);
    }
    return(0);
}

/*
 * This is not a runaway item.  Only Pokes make it change.
 */
HDDEDATA TestHugeXfer(
PXFERINFO pXferInfo,
WORD iFmt)
{
    BOOL fSuccess;
    DWORD ulcb;
    LPBYTE lpData;
    WORD i;
    HDDEDATA hData;

    switch (pXferInfo->wType) {
    case XTYP_POKE:
        ulcb = DdeGetData(pXferInfo->hData, NULL, 0, 0);
        fSuccess = CheckHugeData(pXferInfo->hData);
        if (fSuccess) {
            pszComment = TEXT("Huge poke data successfully received.");
        } else {
            wsprintf(szComment, TEXT("%ld bytes of invalid Huge data received."), ulcb);
            pszComment = szComment;
        }
        InvalidateRect(hwndServer, &rcComment, TRUE);
        InvalidateRect(hwndServer, &rcHugeSize, TRUE);
        if (fSuccess) {
            for (i = 0; i < CFORMATS; i++) {
                if (hDataHuge[i]) {
                    DdeFreeDataHandle(hDataHuge[i]);
                    hDataHuge[i] = 0;
                }
            }
            /*
             * Since callback data handles are only good for the duration of
             * the callback, we must copy the data to our own data handle.
             */
            lpData = DdeAccessData(pXferInfo->hData, &cbHuge);
            hDataHuge[iFmt] = DdeCreateDataHandle(idInst, lpData, cbHuge, 0,
                    pXferInfo->hszItem, pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
            DdeUnaccessData(pXferInfo->hData);
            DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
        }
        return((HDDEDATA)fSuccess);
        break;

    case XTYP_REQUEST:
        pszComment = TEXT("Huge data requested.");
        InvalidateRect(hwndServer, &rcComment, TRUE);
    case XTYP_ADVREQ:
        Delay(RenderDelay, FALSE);
        if (!hDataHuge[iFmt]) {
            cbHuge = (DWORD)rand() * 64L + 0x10000L;
            wsprintf(szComment, TEXT("Generating huge data - length=%ld..."), cbHuge);
            pszComment = szComment;
            InvalidateRect(hwndServer, &rcComment, TRUE);
            UpdateWindow(hwndServer);
            hDataHuge[iFmt] = CreateHugeDataHandle((LONG)cbHuge, 4325, 345, 5,
                    pXferInfo->hszItem,
                    pXferInfo->wFmt, (WORD)(fAppowned ? HDATA_APPOWNED : 0));
            pszComment = TEXT("");
            InvalidateRect(hwndServer, &rcComment, TRUE);
            InvalidateRect(hwndServer, &rcHugeSize, TRUE);
        }
        hData = hDataHuge[iFmt];
        if (!fAppowned)
            hDataHuge[iFmt] = 0;
        return(hData);
        break;

    case XTYP_ADVSTART:
        return((HDDEDATA)1);
    }
    return(0);
}

HDDEDATA HelpXfer(
PXFERINFO pXferInfo,
WORD iFmt)
{
    HDDEDATA hData;

    switch (pXferInfo->wType) {
    case XTYP_REQUEST:
        pszComment = TEXT("Help text requested.");
        InvalidateRect(hwndServer, &rcComment, TRUE);
    case XTYP_ADVREQ:
        if (!hDataHelp[iFmt]) {
           hDataHelp[iFmt] = DdeCreateDataHandle(idInst,
                    (PBYTE)szDdeHelp,
                    (_tcslen(szDdeHelp) + 1) * sizeof(TCHAR),
                    0, pXferInfo->hszItem, pXferInfo->wFmt,
                    fAppowned ? HDATA_APPOWNED : 0);
        }
        hData = hDataHelp[iFmt];
        if (!fAppowned)
            hDataHelp[iFmt] = 0;
        return(hData);
        break;

    case XTYP_ADVSTART:
        return((HDDEDATA)1);
    }
    return(0);
}

/***************************** Private Function ****************************\
*  This creates often used global hszs from standard global strings.
*  It also fills the hsz fields of the topic and item tables.
*
\***************************************************************************/
VOID Hszize()
{
    register ITEMLIST *pItemList;
    int iTopic, iItem;

    hszAppName = DdeCreateStringHandle(idInst, szServer, 0);

    for (iTopic = 0; iTopic < CTOPICS; iTopic++) {
        topicList[iTopic].hszTopic =
                DdeCreateStringHandle(idInst, topicList[iTopic].pszTopic, 0);
        pItemList = topicList[iTopic].pItemList;
        for (iItem = 0; iItem < (int)topicList[iTopic].cItems; iItem++) {
            pItemList[iItem].hszItem =
                    DdeCreateStringHandle(idInst, pItemList[iItem].pszItem, 0);
        }
    }
}

/***************************** Private Function ****************************\
*  This destroys often used global hszs from standard global strings.
*
\***************************************************************************/
VOID UnHszize()
{
    register ITEMLIST *pItemList;
    int iTopic, iItem;

    DdeFreeStringHandle(idInst, hszAppName);

    for (iTopic = 0; iTopic < CTOPICS; iTopic++) {
        DdeFreeStringHandle(idInst, topicList[iTopic].hszTopic);
        pItemList = topicList[iTopic].pItemList;
        for (iItem = 0; iItem < (int)topicList[iTopic].cItems; iItem++) {
            DdeFreeStringHandle(idInst, pItemList[iItem].hszItem);
        }
    }
}


DDEMLSV.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

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

    PROGRAM: Server.c

    PURPOSE: Server template for Windows applications

    FUNCTIONS:

        WinMain() - calls initialization function, processes message loop
        InitApplication() - initializes window data and registers window
        InitInstance() - saves instance handle and creates main window
        MainWndProc() - processes messages
        About() - processes messages for "About" dialog box

    COMMENTS:

        Windows can have several copies of your application running at the
        same time.  The variable hInst keeps track of which instance this
        application is so that processing will be to the correct window.

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include "server.h"                 /* specific to this program              */
#include "huge.h"

DWORD idInst = 0;
CONVCONTEXT CCFilter = { sizeof(CONVCONTEXT), 0, 0, 0, 0L, 0L,
    {
        sizeof(SECURITY_QUALITY_OF_SERVICE),
        SecurityImpersonation,
        SECURITY_STATIC_TRACKING,
        TRUE
    }
};
HANDLE hInst;                       /* current instance                      */
HWND hwndServer;
RECT rcRand;
RECT rcCount;
RECT rcComment;
RECT rcExec;
RECT rcConnCount;
RECT rcRndrDelay;
RECT rcRunaway;
RECT rcAllBlock;
RECT rcNextAction;
RECT rcHugeSize;
RECT rcAppowned;
BOOL fAllBlocked = FALSE;
BOOL fAllEnabled = TRUE;
BOOL fEnableOneCB = FALSE;
BOOL fBlockNextCB = FALSE;
BOOL fTermNextCB = FALSE;
BOOL fAppowned = FALSE;
WORD cRunaway = 0;
WORD RenderDelay = 0;
DWORD count = 0;
WORD seed = 0;
HSZ hszAppName = 0;
TCHAR szClass[] = TEXT("ServerWClass");
TCHAR szTopic[MAX_TOPIC] = TEXT("Test");
TCHAR szServer[MAX_TOPIC] = TEXT("Server");
TCHAR szComment[MAX_COMMENT] = TEXT("");
TCHAR szExec[MAX_EXEC] = TEXT("");
TCHAR *pszComment = szComment;
WORD cyText;
WORD cServers = 0;
HDDEDATA hDataHelp[CFORMATS] = {0};
HDDEDATA hDataCount[CFORMATS] = {0};
HDDEDATA hDataRand[CFORMATS] = {0};
HDDEDATA hDataHuge[CFORMATS] = {0};
DWORD cbHuge = 0;

TCHAR szDdeHelp[] = TEXT("DDEML test server help:\r\n\n")\
    TEXT("The 'Server'(service) and 'Test'(topic) names may change.\r\n\n")\
    TEXT("Items supported under the 'Test' topic are:\r\n")\
    TEXT("\tCount:\tThis value increments on each data change.\r\n")\
    TEXT("\tRand:\tThis value is randomly generated each data change.\r\n")\
    TEXT("\tHuge:\tThis is randomlly generated text data >64k that the\r\n")\
    TEXT("\t\tDDEML test client can verify.\r\n")\
    TEXT("The above items change after any request if in Runaway mode and \r\n")\
    TEXT("can bo POKEed in order to change their values.  POKEed Huge data \r\n")\
    TEXT("must be in a special format to verify the correctness of the data \r\n")\
    TEXT("or it will not be accepted.\r\n")\
    TEXT("If the server is set to use app owned data handles, all data sent \r\n")\
    TEXT("uses HDATA_APPOWNED data handles.")\
    ;

FORMATINFO aFormats[CFORMATS] = {
#ifdef UNICODE
    { 0, TEXT("CF_UNICODETEXT") },       // exception!  predefined format
#else
    { 0, TEXT("CF_TEXT") },       // exception!  predefined format
#endif
    { 0, TEXT("Dummy1")  },
    { 0, TEXT("Dummy2")  },
};

/*
 *          Topic and Item tables supported by this application.
 */

/*   HSZ    PROCEDURE       PSZ        */

ITEMLIST SystemTopicItemList[CSYSTEMITEMS] = {

    { 0, TopicListXfer,  SZDDESYS_ITEM_TOPICS   },
    { 0, ItemListXfer,   SZDDESYS_ITEM_SYSITEMS },
    { 0, sysFormatsXfer, SZDDESYS_ITEM_FORMATS  },
    { 0, HelpXfer,       SZDDESYS_ITEM_HELP},
  };

ITEMLIST TestTopicItemList[CTESTITEMS] = {

    { 0, TestRandomXfer, TEXT("Rand") },   // 0 index
    { 0, TestCountXfer,  TEXT("Count")},   // 1 index
    { 0, TestHugeXfer,   TEXT("Huge") },   // 2 index
    { 0, ItemListXfer,   SZDDESYS_ITEM_SYSITEMS },  // 3 index
  };

/* The system topic is always assumed to be first. */
/*   HSZ   PROCEDURE            #ofITEMS        PSZ     */
TOPICLIST topicList[CTOPICS] = {

    { 0, SystemTopicItemList,   CSYSTEMITEMS,   SZDDESYS_TOPIC},    // 0 index
    { 0, TestTopicItemList,     CTESTITEMS,     szTopic},           // 1 index
  };

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

    FUNCTION: WinMain(HANDLE, HANDLE, LPTSTR, int)

    PURPOSE: calls initialization function, processes message loop

    COMMENTS:

        Windows recognizes this function by name as the initial entry point
        for the program.  This function calls the application initialization
        routine, if no other instance of the program is running, and always
        calls the instance initialization routine.  It then executes a message
        retrieval and dispatch loop that is the top-level control structure
        for the remainder of execution.  The loop is terminated when a WM_QUIT
        message is received, at which time this function exits the application
        instance by returning the value passed by PostQuitMessage().

        If this function must abort before entering the message loop, it
        returns the conventional value NULL.

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

INT WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
INT nCmdShow)
{
    MSG msg;                                 /* message                      */

    if (!hPrevInstance)                  /* Other instances of app running? */
        if (!InitApplication(hInstance)) /* Initialize shared things */
            return (FALSE);              /* Exits if unable to initialize     */

    /* Perform initializations that apply to a specific instance */

    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    /* Acquire and dispatch messages until a WM_QUIT message is received. */

    while (GetMessage(&msg,     /* message structure                      */
            0,                  /* handle of window receiving the message */
            0,                  /* lowest message to examine              */
            0))                 /* highest message to examine             */
        {
        TranslateMessage(&msg);    /* Translates virtual key codes           */
        DispatchMessage(&msg);     /* Dispatches message to window           */
    }

    UnregisterClass(szClass, hInstance);
    return (msg.wParam);           /* Returns the value from PostQuitMessage */
}

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

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

    COMMENTS:

        This function is called at initialization time only if no other
        instances of the application are running.  This function performs
        initialization tasks that can be done once for any number of running
        instances.

        In this case, we initialize a window class by filling out a data
        structure of type WNDCLASS and calling the Windows RegisterClass()
        function.  Since all instances of this application use the same window
        class, we only need to do this when the first instance is initialized.

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

BOOL InitApplication(hInstance)
HANDLE hInstance;                              /* current instance           */
{
    WNDCLASS  wc;

    /* Fill in window class structure with parameters that describe the       */
    /* main window.                                                           */

    wc.style = 0;                       /* Class style(s).                    */
    wc.lpfnWndProc = MainWndProc;       /* Function to retrieve messages for  */
                                        /* windows of this class.             */
    wc.cbClsExtra = 0;                  /* No per-class extra data.           */
    wc.cbWndExtra = 0;                  /* No per-window extra data.          */
    wc.hInstance = hInstance;           /* Application that owns the class.   */
    wc.hIcon = LoadIcon(hInstance, TEXT("server"));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HANDLE)(COLOR_APPWORKSPACE+1);
    wc.lpszMenuName =  TEXT("ServerMenu");   /* Name of menu resource in .RC file. */
    wc.lpszClassName = TEXT("ServerWClass"); /* Name used in call to CreateWindow. */

    /* Register the window class and return success/failure code. */

    return (RegisterClass(&wc));

}

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

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

    COMMENTS:

        This function is called at initialization time for every instance of
        this application.  This function performs initialization tasks that
        cannot be shared by multiple instances.

        In this case, we save the instance handle in a static variable and
        create and display the main program window.

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

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;          /* Current instance identifier.       */
    INT             nCmdShow;           /* Param for first ShowWindow() call. */
{
    INT i;
    RECT Rect;
    TEXTMETRIC metrics;
    HDC hdc;
    TCHAR szTitle[40] =
#ifdef UNICODE
        TEXT("Server|Test (Unicode)");
#else
        TEXT("Server|Test");
#endif

    /* Save the instance handle in static variable, which will be used in  */
    /* many subsequence calls from this application to Windows.            */

    hInst = hInstance;

    /* Create a main window for this application instance.  */

    hwndServer = CreateWindow(
        TEXT("ServerWClass"),                /* See RegisterClass() call.          */
        szTitle,
        WS_OVERLAPPEDWINDOW,            /* Window style.                      */
        CW_USEDEFAULT,                  /* Default horizontal position.       */
        CW_USEDEFAULT,                  /* Default vertical position.         */
        400,
        200,
        NULL,                           /* Overlapped windows have no parent. */
        NULL,                           /* Use the window class menu.         */
        hInstance,                      /* This instance owns this window.    */
        NULL                            /* Pointer not needed.                */
    );

    GetClientRect(hwndServer, (LPRECT) &Rect);

    /* If window could not be created, return "failure" */

    if (!hwndServer)
        return (FALSE);

    hdc = GetDC(hwndServer);
    GetTextMetrics(hdc, &metrics);
    cyText = (WORD)(metrics.tmHeight + metrics.tmExternalLeading);
    ReleaseDC(hwndServer, hdc);

#ifdef UNICODE
    aFormats[0].atom = CF_UNICODETEXT; // exception - predefined.
#else
    aFormats[0].atom = CF_TEXT; // exception - predefined.
#endif
    for (i = 1; i < CFORMATS; i++) {
        aFormats[i].atom = RegisterClipboardFormat(aFormats[i].sz);
    }

    /* Make the window visible; update its client area; and return "success" */

    ShowWindow(hwndServer, nCmdShow);  /* Show the window                        */
    UpdateWindow(hwndServer);          /* Sends WM_PAINT message                 */
    seed = 1;
    srand(1);
    CCFilter.iCodePage = CP_WINANSI;   // initial default codepage
    if (!DdeInitialize(&idInst, (PFNCALLBACK)MakeProcInstance((FARPROC)DdeCallback,
                hInstance), APPCMD_FILTERINITS, 0)) {
        Hszize();
        DdeNameService(idInst, hszAppName, 0, DNS_REGISTER);
        return(TRUE);
    }
    return (FALSE);

}

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

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

        WM_COMMAND    - application menu (About dialog box)
        WM_DESTROY    - destroy window

    COMMENTS:

        To process the IDM_ABOUT message, call MakeProcInstance() to get the
        current instance address of the About() function.  Then call Dialog
        box which will create the box according to the information in your
        server.rc file and turn control over to the About() function.   When
        it returns, free the intance address.

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

LONG  APIENTRY MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;                                /* window handle                   */
UINT message;                         /* type of message                 */
WPARAM wParam;                              /* additional information          */
LPARAM lParam;                              /* additional information          */
{
    switch (message) {
    case WM_INITMENU:
        if (GetMenu(hWnd) != (HMENU)wParam)
            break;

        CheckMenuItem((HMENU)wParam, IDM_BLOCKALLCBS,
                fAllBlocked ? MF_CHECKED : MF_UNCHECKED);
        CheckMenuItem((HMENU)wParam, IDM_UNBLOCKALLCBS,
                fAllEnabled ? MF_CHECKED : MF_UNCHECKED);
        CheckMenuItem((HMENU)wParam, IDM_BLOCKNEXTCB,
                fBlockNextCB ? MF_CHECKED : MF_UNCHECKED);
        CheckMenuItem((HMENU)wParam, IDM_TERMNEXTCB,
                fTermNextCB ? MF_CHECKED : MF_UNCHECKED);
        CheckMenuItem((HMENU)wParam, IDM_RUNAWAY,
                cRunaway ? MF_CHECKED : MF_UNCHECKED);
        CheckMenuItem((HMENU)wParam, IDM_APPOWNED,
                fAppowned ? MF_CHECKED : MF_UNCHECKED);
        break;

    case WM_COMMAND:           /* message: command from application menu */
        switch (LOWORD(wParam)) {
        case IDM_ENABLEONECB:
            DdeEnableCallback(idInst, 0, EC_ENABLEONE);
            fAllBlocked = FALSE;
            fAllEnabled = FALSE;
            InvalidateRect(hwndServer, &rcAllBlock, TRUE);
            break;

        case IDM_TERMNEXTCB:
            fTermNextCB = !fTermNextCB;
            InvalidateRect(hwndServer, &rcNextAction, TRUE);
            break;

        case IDM_BLOCKNEXTCB:
            fBlockNextCB = !fBlockNextCB;
            InvalidateRect(hwndServer, &rcNextAction, TRUE);
            break;

        case IDM_BLOCKALLCBS:
            DdeEnableCallback(idInst, 0, EC_DISABLE);
            fAllBlocked = TRUE;
            fAllEnabled = FALSE;
            InvalidateRect(hwndServer, &rcAllBlock, TRUE);
            break;

        case IDM_UNBLOCKALLCBS:
            DdeEnableCallback(idInst, 0, EC_ENABLEALL);
            fAllEnabled = TRUE;
            fAllBlocked = FALSE;
            InvalidateRect(hwndServer, &rcAllBlock, TRUE);
            break;

        case IDM_APPOWNED:
            fAppowned = !fAppowned;
            if (!fAppowned) {
                WORD iFmt;
                for (iFmt = 0; iFmt < CFORMATS; iFmt++) {
                    if (hDataHuge[iFmt]) {
                        DdeFreeDataHandle(hDataHuge[iFmt]);
                        hDataHuge[iFmt] = 0;
                        InvalidateRect(hwndServer, &rcHugeSize, TRUE);
                    }
                    if (hDataCount[iFmt]) {
                        DdeFreeDataHandle(hDataCount[iFmt]);
                        hDataCount[iFmt] = 0;
                    }
                    if (hDataRand[iFmt]) {
                        DdeFreeDataHandle(hDataRand[iFmt]);
                        hDataRand[iFmt] = 0;
                    }
                    if (hDataHelp[iFmt]) {
                        DdeFreeDataHandle(hDataHelp[iFmt]);
                        hDataHelp[iFmt] = 0;
                    }
                }
            }
            InvalidateRect(hwndServer, &rcAppowned, TRUE);
            break;

        case IDM_RUNAWAY:
            cRunaway = !cRunaway;
            InvalidateRect(hwndServer, &rcRunaway, TRUE);
            if (!cRunaway) {
                break;
            }
            // fall through

        case IDM_CHANGEDATA:
            PostMessage(hwndServer, UM_CHGDATA, 1, 0);  // rand
            PostMessage(hwndServer, UM_CHGDATA, 1, 1);  // count
            break;

        case IDM_RENDERDELAY:
            DoDialog(MAKEINTRESOURCE(DID_VALUEENTRY), (DLGPROC)RenderDelayDlgProc, 0, TRUE);
            InvalidateRect(hwndServer, &rcRndrDelay, TRUE);
            break;

        case IDM_SETSERVER:
            DoDialog(MAKEINTRESOURCE(DID_VALUEENTRY), (DLGPROC)SetServerDlgProc, 0, TRUE);
            break;

        case IDM_SETTOPIC:
            DoDialog(MAKEINTRESOURCE(DID_VALUEENTRY), (DLGPROC)SetTopicDlgProc, 0, TRUE);
            break;

        case IDM_CONTEXT:
            DoDialog(MAKEINTRESOURCE(DID_CONTEXT), (DLGPROC)ContextDlgProc, 0, TRUE);
            break;

        case IDM_ABOUT:
            DoDialog(MAKEINTRESOURCE(DID_ABOUT), (DLGPROC)About, 0, TRUE);
            break;

        case IDM_HELP:
           break;

        default:
            return (DefWindowProc(hWnd, message, wParam, lParam));
        }
        break;

    case WM_PAINT:
        PaintServer(hWnd);
        break;

    case UM_CHGDATA:
        {
            WORD iFmt;

            // wParam = TopicIndex,
            // LOWORD(lParam) = ItemIndex
            // We asynchronously do DdePostAdvise() calls to prevent infinite
            // loops when in runaway mode.
            if (wParam == 1) {  // test topic
                if (lParam == 0) {  // rand item
                    seed = rand();
                    for (iFmt = 0; iFmt < CFORMATS ; iFmt++) {
                        if (hDataRand[iFmt]) {
                            DdeFreeDataHandle(hDataRand[iFmt]);
                            hDataRand[iFmt] = 0;
                        }
                    }
                    InvalidateRect(hwndServer, &rcRand, TRUE);
                    DdePostAdvise(idInst, topicList[wParam].hszTopic,
                            (HSZ)topicList[wParam].pItemList[lParam].hszItem);
                }
                if (lParam == 1) {  // count item
                    count++;
                    for (iFmt = 0; iFmt < CFORMATS ; iFmt++) {
                        if (hDataCount[iFmt]) {
                            DdeFreeDataHandle(hDataCount[iFmt]);
                            hDataCount[iFmt] = 0;
                        }
                    }
                    InvalidateRect(hwndServer, &rcCount, TRUE);
                    DdePostAdvise(idInst, topicList[wParam].hszTopic,
                            (HSZ)topicList[wParam].pItemList[lParam].hszItem);
                }
                // Huge item does not runaway - too slow.
            }
            if (cRunaway) {
                Delay(50, TRUE);
                        // This gives enough time for the system to remain
                        // useable in runaway mode.
                PostMessage(hwndServer, UM_CHGDATA, wParam, lParam);
            }
        }
        break;

    case WM_DESTROY:                  /* message: window being destroyed */
        if (fAppowned)
            SendMessage(hwndServer, WM_COMMAND, (WPARAM)MAKELONG(IDM_APPOWNED, 0), (LONG)(0));
        DdeNameService(idInst, 0, 0, DNS_UNREGISTER); // unregister all services
        UnHszize();
        DdeUninitialize(idInst);
        PostQuitMessage(0);
        break;

    default:
        return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return(0);
}

VOID Delay(
DWORD delay,
BOOL fModal)
{
    MSG msg;
    delay = GetCurrentTime() + delay;
    while (GetCurrentTime() < delay) {
        if (fModal && PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
}

/*
 * This function not only paints the server client area with current info,
 * it also has the side effect of setting the global RECTs that bound each
 * info area.  This way flashing is reduced.
 */
VOID PaintServer(
HWND hwnd)
{
    PAINTSTRUCT ps;
    RECT rc;
    TCHAR szT[MAX_COMMENT];

    BeginPaint(hwnd, &ps);
    SetBkMode(ps.hdc, TRANSPARENT);
    GetClientRect(hwnd, &rc);
    rc.bottom = rc.top + cyText;    // all rects are cyText in height.

    rcComment = rc;
    DrawTextLine(ps.hdc, &ps.rcPaint, &rc, pszComment);

    wsprintf(szT, TEXT("# of connections:%d"), cServers);
    rcConnCount = rc;
    DrawTextLine(ps.hdc, &ps.rcPaint, &rc, szT);

    szT[0] = TEXT('\0');
    rcAllBlock = rc;
    if (fAllEnabled)
        DrawTextLine(ps.hdc, &ps.rcPaint, &rc, TEXT("All Conversations are Enabled."));
    else if (fAllBlocked)
        DrawTextLine(ps.hdc, &ps.rcPaint, &rc, TEXT("All Conversations are Blocked."));
    else
        DrawTextLine(ps.hdc, &ps.rcPaint, &rc, szT);

    rcNextAction = rc;
    if (fBlockNextCB)
        DrawTextLine(ps.hdc, &ps.rcPaint, &rc, TEXT("Next callback will block."));
    else if (fTermNextCB)
        DrawTextLine(ps.hdc, &ps.rcPaint, &rc, TEXT("Next callback will terminate."));
    else
        DrawTextLine(ps.hdc, &ps.rcPaint, &rc, szT);

    wsprintf(szT, TEXT("Count item = %ld"), count);
    rcCount = rc;
    DrawTextLine(ps.hdc, &ps.rcPaint, &rc, szT);

    wsprintf(szT, TEXT("Rand item = %d"), seed);
    rcRand = rc;
    DrawTextLine(ps.hdc, &ps.rcPaint, &rc, szT);

    wsprintf(szT, TEXT("Huge item size = %ld"), cbHuge);
    rcHugeSize = rc;
    DrawTextLine(ps.hdc, &ps.rcPaint, &rc, szT);

    wsprintf(szT, TEXT("Render delay is %d milliseconds."), RenderDelay);
    rcRndrDelay = rc;
    DrawTextLine(ps.hdc, &ps.rcPaint, &rc, szT);

    rcExec = rc;
    DrawTextLine(ps.hdc, &ps.rcPaint, &rc, szExec);

    rcRunaway = rc;
    DrawTextLine(ps.hdc, &ps.rcPaint, &rc, cRunaway ? TEXT("Runaway active.") : TEXT(""));

    rcAppowned = rc;
    DrawTextLine(ps.hdc, &ps.rcPaint, &rc, fAppowned ? TEXT("Using AppOwned Data Handles.") : TEXT(""));

    EndPaint(hwnd, &ps);
}

VOID DrawTextLine(
HDC hdc,
RECT *prcClip,
RECT *prcText,
PTSTR psz)
{
    RECT rc;

    if (IntersectRect(&rc, prcText, prcClip)) {
        DrawText(hdc, psz, -1, prcText,
                DT_LEFT | DT_EXTERNALLEADING | DT_SINGLELINE | DT_EXPANDTABS |
                DT_NOCLIP | DT_NOPREFIX);
    }
    OffsetRect(prcText, 0, cyText);
}


DIALOG.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <string.h>
#include <stdio.h>
#include "server.h"
#include "huge.h"

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : DoDialog()                                                 *
 *                                                                          *
 *  PURPOSE    : Generic dialog invocation routine.  Handles procInstance   *
 *               stuff, focus management and param passing.                 *
 *  RETURNS    : result of dialog procedure.                                *
 *                                                                          *
 ****************************************************************************/
INT FAR DoDialog(
LPTSTR lpTemplateName,
DLGPROC lpDlgProc,
LPARAM param,
BOOL fRememberFocus)
{
    INT iRet;
    HWND hwndFocus;
    WORD cRunawayT;

    cRunawayT = cRunaway;
    cRunaway = 0;           // turn off runaway during dialogs.

    if (fRememberFocus)
        hwndFocus = GetFocus();
    lpDlgProc = MakeProcInstance(lpDlgProc, hInst);
    iRet = DialogBoxParam(hInst, lpTemplateName, hwndServer, lpDlgProc, param);
    FreeProcInstance(lpDlgProc);
    if (fRememberFocus)
        SetFocus(hwndFocus);

    cRunaway = cRunawayT;   // restore runaway state.
    return iRet;
}

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

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

        WM_INITDIALOG - initialize dialog box
        WM_COMMAND    - Input received

    COMMENTS:

        No initialization is needed for this particular dialog box, but TRUE
        must be returned to Windows.

        Wait for user to click on "Ok" button, then close the dialog box.

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

BOOL  APIENTRY About(hDlg, message, wParam, lParam)
HWND hDlg;                                /* window handle of the dialog box */
UINT message;                         /* type of message                 */
WPARAM wParam;                              /* message-specific information    */
LPARAM lParam;
{
    switch (message) {
        case WM_INITDIALOG:                /* message: initialize dialog box */
            return (TRUE);

        case WM_COMMAND:                      /* message: received a command */
            if (LOWORD(wParam) == IDOK                /* "OK" box selected?          */
                || LOWORD(wParam) == IDCANCEL) {      /* System menu close command? */
                EndDialog(hDlg, TRUE);        /* Exits the dialog box        */
                return (TRUE);
            }
            break;
    }
    return (FALSE);                           /* Didn't process a message    */
}

BOOL  APIENTRY RenderDelayDlgProc(
HWND          hwnd,
register UINT msg,
register WPARAM wParam,
LPARAM lParam)
{
    switch (msg){
    case WM_INITDIALOG:
        SetWindowText(hwnd, TEXT("Data Render Delay"));
        SetDlgItemInt(hwnd, IDEF_VALUE, RenderDelay, FALSE);
        SetDlgItemText(hwnd, IDTX_VALUE, TEXT("Delay in milliseconds:"));
        return(1);
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
            RenderDelay = GetDlgItemInt(hwnd, IDEF_VALUE, NULL, FALSE);
            // fall through
        case IDCANCEL:
            EndDialog(hwnd, 0);
            break;

        default:
            return(FALSE);
        }
        break;
    }
    return(FALSE);
}

BOOL  APIENTRY SetTopicDlgProc(
HWND          hwnd,
register UINT msg,
register WPARAM wParam,
LPARAM lParam)
{
    TCHAR szT[MAX_TOPIC + 20];

    switch (msg){
    case WM_INITDIALOG:
        SetWindowText(hwnd, TEXT("Set Topic Dialog"));
        SetDlgItemText(hwnd, IDEF_VALUE, szTopic);
        SetDlgItemText(hwnd, IDTX_VALUE, TEXT("Topic:"));
        return(1);
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
            DdeFreeStringHandle(idInst, topicList[1].hszTopic);
            GetDlgItemText(hwnd, IDEF_VALUE, szTopic, MAX_TOPIC);
            topicList[1].hszTopic = DdeCreateStringHandle(idInst, szTopic, 0);
            _tcscpy(szT, szServer);
            _tcscat(szT, TEXT(" | "));
            _tcscat(szT, szTopic);
#ifdef UNICODE
            _tcscat(szT, TEXT(" (Unicode)"));
#endif
            SetWindowText(hwndServer, szT);
            // fall through
        case IDCANCEL:
            EndDialog(hwnd, 0);
            break;

        default:
            return(FALSE);
        }
        break;
    }
    return(FALSE);
}

BOOL  APIENTRY SetServerDlgProc(
HWND          hwnd,
register UINT msg,
register WPARAM wParam,
LPARAM lParam)
{
    TCHAR szT[MAX_TOPIC + 20];

    switch (msg){
    case WM_INITDIALOG:
        SetWindowText(hwnd, TEXT("Set Server Name Dialog"));
        SetDlgItemText(hwnd, IDEF_VALUE, szServer);
        SetDlgItemText(hwnd, IDTX_VALUE, TEXT("Server:"));
        return(1);
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
            GetDlgItemText(hwnd, IDEF_VALUE, szServer, MAX_TOPIC);
            DdeNameService(idInst, hszAppName, 0, DNS_UNREGISTER);
            DdeFreeStringHandle(idInst, hszAppName);
            hszAppName = DdeCreateStringHandle(idInst, szServer, 0);
            DdeNameService(idInst, hszAppName, 0, DNS_REGISTER);
            _tcscpy(szT, szServer);
            _tcscat(szT, TEXT(" | "));
            _tcscat(szT, szTopic);
#ifdef UNICODE
            _tcscat(szT, TEXT(" (Unicode)"));
#endif
            SetWindowText(hwndServer, szT);
            // fall through
        case IDCANCEL:
            EndDialog(hwnd, 0);
            break;

        default:
            return(FALSE);
        }
        break;
    }
    return(FALSE);
}

BOOL  APIENTRY ContextDlgProc(
HWND hwnd,
register UINT msg,
register WPARAM wParam,
LPARAM lParam)
{
    BOOL fSuccess;

    switch (msg){
    case WM_INITDIALOG:
        SetDlgItemInt(hwnd, IDEF_FLAGS, CCFilter.wFlags, FALSE);
        SetDlgItemInt(hwnd, IDEF_COUNTRY, CCFilter.wCountryID, FALSE);
        SetDlgItemInt(hwnd, IDEF_CODEPAGE, CCFilter.iCodePage, TRUE);
        SetDlgItemInt(hwnd, IDEF_LANG, LOWORD(CCFilter.dwLangID), FALSE);
        SetDlgItemInt(hwnd, IDEF_SECURITY, LOWORD(CCFilter.dwSecurity), FALSE);
        CheckRadioButton(hwnd, IDRB_IL_ANON, IDRB_IL_DELEGATE,
                IDRB_IL_ANON + (int)CCFilter.qos.ImpersonationLevel);
        return(1);
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
            CCFilter.wFlags = GetDlgItemInt(hwnd, IDEF_FLAGS, &fSuccess, FALSE);
            if (!fSuccess) return(0);
            CCFilter.wCountryID = GetDlgItemInt(hwnd, IDEF_COUNTRY, &fSuccess, FALSE);
            if (!fSuccess) return(0);
            CCFilter.iCodePage = GetDlgItemInt(hwnd, IDEF_CODEPAGE, &fSuccess, TRUE);
            if (!fSuccess) return(0);
            CCFilter.dwLangID = (DWORD)GetDlgItemInt(hwnd, IDEF_LANG, &fSuccess, FALSE);
            if (!fSuccess) return(0);
            CCFilter.dwSecurity = (DWORD)GetDlgItemInt(hwnd, IDEF_SECURITY, &fSuccess, FALSE);
            if (!fSuccess) return(0);

            if (IsDlgButtonChecked(hwnd, IDRB_IL_ANON)) {
                CCFilter.qos.ImpersonationLevel = SecurityAnonymous;
            } else if (IsDlgButtonChecked(hwnd, IDRB_IL_ID)) {
                CCFilter.qos.ImpersonationLevel = SecurityIdentification;
            } else if (IsDlgButtonChecked(hwnd, IDRB_IL_IMP)) {
                CCFilter.qos.ImpersonationLevel = SecurityImpersonation;
            } else if (IsDlgButtonChecked(hwnd, IDRB_IL_DELEGATE)) {
                CCFilter.qos.ImpersonationLevel = SecurityDelegation;
            }

            // fall through
        case IDCANCEL:
            EndDialog(hwnd, 0);
            break;

        default:
            return(FALSE);
        }
        break;
    }
    return(FALSE);
}


DIALOG.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define     IDEF_VALUE  101
#define     IDTX_VALUE  102
#define     IDEF_DATA   103
#define     IDBN_GENHUGE 104

#define DID_ABOUT                   900
#define DID_TEXTDATAENTRY           800
#define DID_VALUEENTRY              300
#define DID_CONTEXT                 906
#define IDEF_FLAGS                  908
#define IDEF_COUNTRY                909
#define IDEF_CODEPAGE               910
#define IDEF_LANG                   911
#define IDEF_SECURITY               912
#define IDRB_IL_ANON                913
#define IDRB_IL_ID                  914
#define IDRB_IL_IMP                 915
#define IDRB_IL_DELEGATE            916


HUGE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/***************************************************************************
 *                                                                         *
 *  MODULE      : huge.c                                                   *
 *                                                                         *
 *  PURPOSE     : This contains functions useful for generating and        *
 *                verifying huge text data blocks.                         *
 *                                                                         *
 ***************************************************************************/

#include <string.h>
#include <tchar.h>
#include <stdio.h>
#include <windows.h>
#include <ddeml.h>
#include "huge.h"

extern DWORD idInst;
#define BUFSZ   435

LONG lseed, lmult, ladd;
TCHAR szT[BUFSZ];

VOID SetMyRand(LONG seed, LONG mult, LONG add);
TCHAR MyRand(VOID);
BOOL RandTest(LONG length, LONG seed, LONG mult, LONG add);

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : SetMyRand()                                                *
 *                                                                          *
 *  PURPOSE    : Transfers random sequence generation variables to globals. *
 *                                                                          *
 ****************************************************************************/
VOID SetMyRand(
LONG seed,
LONG mult,
LONG add)
{
    lseed = seed;
    lmult = mult;
    ladd = add;
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : MyRand()                                                   *
 *                                                                          *
 *  PURPOSE    : Generates the next random character in a sequence.         *
 *                                                                          *
 *  RETURNS    : the character generated                                    *
 *                                                                          *
 ****************************************************************************/
TCHAR MyRand()
{
    TCHAR c;

    lseed = lseed * lmult + ladd;
    c = (TCHAR)(LOWORD(lseed) ^ HIWORD(lseed));
    return((TCHAR)((c & (TCHAR)0x4f) + TEXT(' ')));   // 0x20 - 0x6f - all printable
}

/*
 * This function allocates and fills a HUGE data handle with a verifiable
 * text string.
 *
 * The format of the text string is:
 * "<length>=<seed>*<mult>+<add>;---data of length <length>---\0"
 * all values are stored in base 16 numbers.
 */
/****************************************************************************
 *                                                                          *
 *  FUNCTION   : CreateHugeDataHandle()                                     *
 *                                                                          *
 *  PURPOSE    : Generates a huge pseudo-random sequence of printable       *
 *               characters of the length given and places then into        *
 *               a DDEML data handle.                                       *
 *                                                                          *
 *  RETURNS    : The data handle created or 0 on failure.                   *
 *                                                                          *
 ****************************************************************************/
HDDEDATA CreateHugeDataHandle(
LONG length,
LONG seed,
LONG mult,
LONG add,
HSZ hszItem,
UINT wFmt,
WORD afCmd)
{
    register WORD cb;
    HDDEDATA hData;
    DWORD cbData;
    TCHAR *psz;

    wsprintf(szT, TEXT("%ld=%ld*%ld+%ld;"), length, seed, mult, add);
    cb = (_tcslen(szT) + 1) * sizeof(TCHAR);
    hData = DdeCreateDataHandle(idInst, (PBYTE)szT, cb, 0,
            hszItem, wFmt, afCmd);
    if (hData)
        hData = DdeAddData(hData, NULL, 0, cb + length);
    cbData = cb;
    SetMyRand(seed, mult, add);
    while (hData && (length > 0)) {
        psz = szT;
        cb = BUFSZ;
        while (cb--)
            *psz++ = MyRand();
        hData = DdeAddData(hData, (PBYTE)szT, min(length, BUFSZ), cbData);
        cbData += BUFSZ;
        length -= BUFSZ;
    }
    return(hData);
}

/****************************************************************************
 *                                                                          *
 *  FUNCTION   : CheckHugeData()                                            *
 *                                                                          *
 *  PURPOSE    : Verifies the correctness of a pseudo-random character      *
 *               sequence generated by CreateHugeData.                      *
 *                                                                          *
 *  RETURNS    : TRUE if verified ok, FALSE otherwise.                      *
 *                                                                          *
 ****************************************************************************/
BOOL CheckHugeData(
HDDEDATA hData)
{
    LONG length;
    LONG seed;
    LONG mult;
    LONG add;
    TCHAR *psz;
    DWORD cbOff;
    WORD cb;

    if (!DdeGetData(hData, (PBYTE)szT, BUFSZ, 0))
        return(FALSE);
    szT[BUFSZ - 1] = TEXT('\0');
    psz = _tcschr(szT, TEXT(';'));
    if (psz == NULL)
        return(FALSE);
    *psz = TEXT('\0');

    if (_stscanf(szT, TEXT("%ld=%ld*%ld+%ld"), &length, &seed, &mult, &add) != 4)
        return(FALSE);

    if (length < 0)
        return(FALSE);
    SetMyRand(seed, mult, add);
    cbOff = (_tcslen(szT) + 1) * sizeof(TCHAR);
    while (length > 0) {
        DdeGetData(hData, (PBYTE)szT, BUFSZ, cbOff);
        psz = szT;
        cb = BUFSZ;
        while (length-- && cb--)
            if (*psz++ != MyRand())
                return(FALSE);
        cbOff += BUFSZ;
        length -= BUFSZ;
    }
    return(TRUE);
}

#if 0
/****************************************************************************
 *                                                                          *
 *  FUNCTION   : RandTest()                                                 *
 *                                                                          *
 *  PURPOSE    : Verifies the correctness of CreateHugeDataHandle() and     *
 *               CheckHugeData().                                           *
 *                                                                          *
 *  RETURNS    :                                                            *
 *                                                                          *
 ****************************************************************************/
BOOL RandTest(
LONG length,
LONG seed,
LONG mult,
LONG add)
{
    HDDEDATA hData;
    BOOL fSuccess;

    hData = CreateHugeDataHandle(length, seed, mult, add, 0, 1, 0);
    if (!hData)
        return(FALSE);
    fSuccess = CheckHugeData(hData);
    DdeFreeDataHandle(hData);
    return(fSuccess);
}
#endif


HUGE.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// header file for HUGE data handle testing module

// PROCS

HDDEDATA CreateHugeDataHandle(LONG length, LONG seed, LONG mult, LONG add,
        HSZ hszItem, UINT wFmt, WORD afCmd);
BOOL CheckHugeData(HDDEDATA hData);


SERVER.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <ddeml.h>
#include <tchar.h>

#define IDM_ABOUT 100
#define IDM_BLOCKALLCBS 200
#define IDM_ENABLEONECB 201
#define IDM_BLOCKNEXTCB 202
#define IDM_TERMNEXTCB  203
#define IDM_RUNAWAY     204
#define IDM_CHANGEDATA  205
#define IDM_RENDERDELAY 206
#define IDM_SETTOPIC    207
#define IDM_SETSERVER   208
#define IDM_UNBLOCKALLCBS 209
#define IDM_HELP        210
#define IDM_CONTEXT     211
#define IDM_APPOWNED    212

#define IDS_BADLENGTH   1

#define MAX_TOPIC       20
#define MAX_COMMENT     40
#define MAX_EXEC        40
#define CCHARS          80
#define CLINES          40

#define UM_CHGDATA      (WM_USER + 435)

#define CSYSTEMITEMS 4
#define CTESTITEMS  4
#define CTOPICS     2

#include "dialog.h"

/****** Structrues ******/

// predefined format list item

typedef struct {
    ATOM atom;
    PTSTR sz;
} FORMATINFO;
#define CFORMATS 3

extern FORMATINFO aFormats[];

typedef struct _XFERINFO {
    UINT wType;
    UINT wFmt;
    HCONV hConv;
    HSZ hszTopic;
    HSZ hszItem;
    HDDEDATA hData;
    DWORD lData1;
    DWORD lData2;
} XFERINFO;
typedef XFERINFO *PXFERINFO;

typedef struct _ITEMLIST {
    HSZ hszItem;
    HDDEDATA (*npfnCallback)(PXFERINFO, WORD);
    PTSTR pszItem;
} ITEMLIST;

typedef struct _TOPICLIST {
    HSZ hszTopic;
    ITEMLIST *pItemList;
    UINT cItems;
    PTSTR pszTopic;
} TOPICLIST;

typedef struct {    // used to passinfo to/from TextDataEntryDlgProc and
    HDDEDATA hDdeData;
    UINT wFmt;
    HSZ hszItem;
} XACT;

// GLOBALS

extern DWORD idInst;
extern CONVCONTEXT CCFilter;
extern HANDLE hInst;
extern HWND hwndServer;
extern RECT rcRand;
extern RECT rcCount;
extern RECT rcComment;
extern RECT rcExec;
extern RECT rcConnCount;
extern RECT rcRndrDelay;
extern RECT rcRunaway;
extern RECT rcAllBlock;
extern RECT rcNextAction;
extern RECT rcHugeSize;
extern RECT rcAppowned;
extern BOOL fAllBlocked;
extern BOOL fAllEnabled;
extern BOOL fEnableOneCB;
extern BOOL fBlockNextCB;
extern BOOL fTermNextCB;
extern BOOL fAppowned;
extern WORD cRunaway;
extern WORD RenderDelay;
extern DWORD count;
extern HSZ hszAppName;
extern TCHAR szClass[];
extern TCHAR szTopic[MAX_TOPIC];
extern TCHAR szServer[MAX_TOPIC];
extern TCHAR szComment[MAX_COMMENT];
extern TCHAR szExec[MAX_EXEC];
extern TCHAR *pszComment;
extern WORD seed;
extern WORD cyText;
extern WORD cServers;
extern HDDEDATA hDataHelp[CFORMATS];
extern HDDEDATA hDataCount[CFORMATS];
extern HDDEDATA hDataRand[CFORMATS];
extern HDDEDATA hDataHuge[CFORMATS];
extern DWORD cbHuge;

extern TCHAR szDdeHelp[];
extern FORMATINFO aFormats[CFORMATS];
extern ITEMLIST SystemTopicItemList[CSYSTEMITEMS];
extern ITEMLIST TestTopicItemList[CTESTITEMS];
extern TOPICLIST topicList[CTOPICS];

// FUNCTIONS

BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, INT);
INT FAR DoDialog(LPTSTR lpTemplateName, DLGPROC lpDlgProc, LPARAM param, BOOL fRememberFocus);
LONG  APIENTRY MainWndProc(HWND, UINT, WPARAM, LONG);
BOOL  APIENTRY About(HWND, UINT, WPARAM, LONG);
BOOL  APIENTRY RenderDelayDlgProc(HWND, UINT, WPARAM, LONG);
BOOL  APIENTRY SetTopicDlgProc(HWND, UINT, WPARAM, LONG);
BOOL  APIENTRY SetServerDlgProc(HWND, UINT, WPARAM, LONG);
BOOL  APIENTRY ContextDlgProc(HWND, UINT, WPARAM, LONG);
VOID PaintServer(HWND hwnd);
VOID DrawTextLine(HDC hdc, RECT *prcClip, RECT *prcText, PTSTR psz);
HDDEDATA CALLBACK DdeCallback(UINT wType, UINT wFmt, HCONV hConv, HSZ hszTopic,
        HSZ hszItem, HDDEDATA hData, DWORD lData1, DWORD lData2);
HDDEDATA TopicListXfer(PXFERINFO pXferInfo, WORD iFmt);
HDDEDATA ItemListXfer(PXFERINFO pXferInfo, WORD iFmt);
HDDEDATA sysFormatsXfer(PXFERINFO pXferInfo, WORD iFmt);
HDDEDATA TestRandomXfer(PXFERINFO pXferInfo, WORD iFmt);
HDDEDATA TestCountXfer(PXFERINFO pXferInfo, WORD iFmt);
HDDEDATA TestHugeXfer(PXFERINFO pXferInfo, WORD iFmt);
HDDEDATA HelpXfer(PXFERINFO pXferInfo, WORD iFmt);
VOID Hszize(VOID);
VOID UnHszize(VOID);
VOID Delay(DWORD delay, BOOL fModal);


CHILD.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright (C) 1992-1996 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/* Standard output handle/standard error handle redirection test
program */

#include <stdio.h>
#include <io.h>
#include <string.h>
#include <windows.h>

/* This function opens up a handle to CONOUT$ so that we can be
guaranteed to get error messages to the console even if the standard
handles are redirected. */

void myError(HANDLE h, char *api, int line)
{
  char buf[512];
  BOOL fSuccess;
  DWORD cBytesWritten;
  HANDLE hConout;

  sprintf(buf, "%s: Error %d from %s on line %d\n", __FILE__, GetLastError(),
      api, line);
  fSuccess = WriteFile(h, buf, strlen(buf), &cBytesWritten, NULL);
  if (!fSuccess)
    {
    hConout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, NULL);
    sprintf(buf, "%s: Error %d from %s on line %d\n", __FILE__,
      GetLastError(), api, line);
    WriteFile(hConout, buf, strlen(buf), &cBytesWritten, NULL);
    CloseHandle(hConout);
    }
  return;
}

/* get the standard output handle and standard error handle and
write a message to each */

int main()
{
  DWORD cBytesWritten;
  HANDLE hStdErr, hStdOut, hConout;
  BOOL fSuccess;
  char *stdErrMsg = "Hello from child to standard error!\n";
  char *stdOutMsg = "Hello from child to standard output!\n";

  hConout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
      FILE_ATTRIBUTE_NORMAL, NULL);
  if ((int) hConout == -1)
    Beep(5000,500); /* no reliable way of reporting errors to the console */
                    /* if we can't open a console handle */
  hStdErr = GetStdHandle(STD_ERROR_HANDLE);
  if (hStdErr == INVALID_HANDLE_VALUE)
    myError(hConout, "GetStdHandle", __LINE__);
  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  if (hStdOut == INVALID_HANDLE_VALUE)
    myError(hConout, "GetStdHandle", __LINE__);
  fSuccess = WriteFile(hStdErr, stdErrMsg, strlen(stdErrMsg),
      &cBytesWritten, NULL);
  if (!fSuccess)
    myError(hConout, "WriteFile", __LINE__);
  fSuccess = WriteFile(hStdOut, stdOutMsg, strlen(stdOutMsg),
      &cBytesWritten, NULL);
  if (!fSuccess)
    myError(hConout, "WriteFile", __LINE__);
  CloseHandle(hConout);
  return(0);
}


INHERIT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright (C) 1992-1996 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/********************************************************************
* This program is to demonstrate the use of anonymous pipes used as *
* stdout and stderr replacements. One of two techniques can be      *
* chose to do this: the SetStdHandle technique or the               *
* STARTF_USESTDHANDLES technique. The SetStdHandle technique sets   *
* the standard output handles to the pipe that we will read from,   *
* which the child will inherit. The STARTF_USESTDHANDLES technique  *
* passes the pipe handles to the child as standard handles via the  *
* STARTUPINFO structure. The STARTF_USESTDHANDLES technique *must*  *
* be used for "console-less" processes such as GUI applications or  *
* detached processes.                                               *
*                                                                   *
* This program demonstrates the use of the following Win32 APIs:    *
*   CreatePipe, CreateProcess.                                      *
*                                                                   *
* This program also uses the following Win32 APIs:                  *
*   GetLastError, CreateFile, CloseHandle, CreateProcess, ReadFile, *
*   WriteFile.                                                      *
*                                                                   *
*                                                                   *
* Execution instructions:                                           *
*                                                                   *
*   inherit <trace file> <command to execute>                       *
*   trace file is the name of the file where the stdout             *
*     and stderr of command will be redirected                      *
*                                                                   *
*   command to execute can be either an external executable or an   *
*     internal cmd.exe command.                                     *
*                                                                   *
*   Examples:                                                       *
*                                                                   *
*   inherit chkdsk.dat chkdsk d:                                    *
*   inherit nmake.txt nmake /f foo.mak                              *
*                                                                   *
********************************************************************/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* define USESTDHANDLES to use the new technique of passing the
standard handles to the child process via the STARTUPINFO structure.
This technique must be used for "console-less" parents such as GUI
applications or detached applications. */

#define USESTDHANDLES

/* Standard error macro for reporting API errors */
//#define PERR(bSuccess, api) {if (!(bSuccess)) printf("%s: Error %d from %s
//    on line %d\n", __FILE__, GetLastError(), api, __LINE__);}

void PERR(BOOL bSuccess, char * api);

int main(int argc, char *argv[])
{
  char chReadBuffer[64];  /* pipe read buffer */
  BOOL bSuccess;  /* BOOL return code for APIs */
  int j;
  HANDLE hOutFile;  /* handle to log file */
  /* handles to the anonymous pipe */
  HANDLE hReadPipe, hWritePipe, hWritePipe2;
  char szArgs[256];  /* child process argument buffer */
  char *p;  /* temporary pointer into szArgs */
  DWORD cchReadBuffer;  /* number of bytes read or to be written */
  STARTUPINFO si;  /* for CreateProcess call */
  PROCESS_INFORMATION pi;  /* for CreateProcess call */
  SECURITY_ATTRIBUTES saPipe;  /* security for anonymous pipe */
  OSVERSIONINFO os; /* operating system specs */

  os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); // required assignment

  // check if running on Windows NT, if not, display notice and terminate
  if ( GetVersionEx(&os) )
  {
      if( os.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS &&
          os.dwPlatformId != VER_PLATFORM_WIN32_NT)
      {
           MessageBox( NULL,
           "This sample application can only run on Windows NT and Windows 95.\n"
           "This application will now terminate.",
           "Inherit",
           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND );

           return( 1 );
      }
  }
  else
  {
       MessageBox( NULL,
       "Unable to test for OS version.",
       "Inherit Error",
       MB_OK | MB_ICONSTOP | MB_SETFOREGROUND );

       return( 1 );
  }

  if (argc < 3)
    {
    puts("format: inherit <trace file> <command to execute>");
    puts("trace file is the name of the file where the stdout");
    puts("and stderr of command will be redirected\n");
    puts("command to execute is command line of the function");
    puts("you wish to perform.\n");
    puts("Examples:\n");
    puts("  inherit trace.txt chkdsk d:");
    puts("  inherit trace.txt nmake /f foo.mak");
    return(1);
    }

  /* create the log file where we will save all output from child */
  hOutFile = CreateFile(argv[1],  /* file to open */
      GENERIC_WRITE,  /* access mode */
      FILE_SHARE_READ,  /* share mode */
      NULL,  /* security attributes */
      CREATE_ALWAYS,  /* creation flags - trash existing file */
      FILE_ATTRIBUTE_NORMAL,  /* file attributes */
      NULL);
  PERR(hOutFile != INVALID_HANDLE_VALUE, "CreateFile");

  /* set up the security attributes for the anonymous pipe */
  saPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
  saPipe.lpSecurityDescriptor = NULL;
  /* In order for the child to be able to write to the pipe, the handle */
  /* must be marked as inheritable by setting this flag: */
  saPipe.bInheritHandle = TRUE;

  /* create the anonymous pipe */
  bSuccess = CreatePipe(&hReadPipe,  /* read handle */
      &hWritePipe,  /* write handle, used as stdout by child */
      &saPipe,  /* security descriptor */
      0);  /* pipe buffer size */
  PERR(bSuccess, "CreatePipe");

  /* Now we need to change the inheritable property for the readable
  end of the pipe so that the child will not inherit that handle as
  a "garbage" handle. This will keep us from having extra,
  unclosable handles to the pipe. Alternatively, we could have
  opened the pipe with saPipe.bInheritHandle = FALSE and changed the
  inherit property on the *write* handle of the pipe to TRUE. */

  bSuccess = DuplicateHandle(GetCurrentProcess(), /* source process */
      hReadPipe, /* handle to duplicate */
      GetCurrentProcess(), /* destination process */
      NULL, /* new handle - don't want one, change original handle */
      0, /* new access flags - ignored since DUPLICATE_SAME_ACCESS */
      FALSE, /* make it *not* inheritable */
      DUPLICATE_SAME_ACCESS);
  PERR(bSuccess, "DuplicateHandle");

  /* I most cases you can get away with using the same anonymous
  pipe write handle for both the child's standard output and
  standard error, but this may cause problems if the child app
  explicitly closes one of its standard output or error handles. If
  that happens, the anonymous pipe will close, since the child's
  standard output and error handles are really the same handle. The
  child won't be able to write to the other write handle since the
  pipe is now gone, and parent reads from the pipe will return
  ERROR_BROKEN_PIPE and child output will be lost. To solve this
  problem, simply duplicate the write end of the pipe to create
  another distinct, separate handle to the write end of the pipe.
  One pipe write handle will serve as standard out, the other as
  standard error. Now *both* write handles must be closed before the
  write end of the pipe actually closes. */

  bSuccess = DuplicateHandle(GetCurrentProcess(), /* source process */
      hWritePipe, /* handle to duplicate */
      GetCurrentProcess(), /* destination process */
      &hWritePipe2, /* new handle, used as stderr by child */
      0, /* new access flags - ignored since DUPLICATE_SAME_ACCESS */
      TRUE, /* it's inheritable */
      DUPLICATE_SAME_ACCESS);
  PERR(bSuccess, "DuplicateHandle");

  /* Set up the STARTUPINFO structure for the CreateProcess() call */
  memset(&si, 0, sizeof(si));
  si.cb = sizeof(si);

  /* Set up the command-line buffer for the child for CreateProcess() */
  memset(szArgs, 0, sizeof(szArgs));
  strcpy(szArgs, argv[2]);
  if (strchr(szArgs, '.') == NULL)  /* does it have a '.'? */
    strcat(szArgs, ".exe");  /* if not, assume it's an .exe */
  strcat(szArgs, " ");
  p = strchr(szArgs, 0);  /* point to the terminating null */
  for (j = 3; j < argc; j++)
    {
    strcat(p, argv[j]);
    /* the program and parameters are delimited by spaces */
    strcat(p, " ");
    }

#ifdef USESTDHANDLES
  /* If using the STARTUPINFO STARTF_USESTDHANDLES flag, be sure to
  set the CreateProcess fInheritHandles parameter too TRUE so that
  the file handles specified in the STARTUPINFO structure will be
  inheritied by the child. Note that we don't specify a standard
  input handle; the child will not inherit a valid input handle, so
  if it reads from stdin, it will encounter errors. */

  si.hStdInput = hWritePipe2; /* hStdInput needs a valid handle in case it is checked by the child */
  si.hStdOutput = hWritePipe; /* write end of the pipe */
  si.hStdError = hWritePipe2; /* duplicate of write end of the pipe */
  si.dwFlags = STARTF_USESTDHANDLES;
#else
  /* If we're not using the STARTF_USESTDHANDLES flag, set the
  standard output and error handles to the end of the pipe we want
  the child to inherit with SetStdHandle(). For this program, we
  don't want standard input inherited so we'll also change the
  handle inheritance property of standard input so that it is not
  inherited */
  bSuccess = SetStdHandle(STD_INPUT_HANDLE, hWritePipe2);
  PERR(bSuccess, "SetStdHandle");
  bSuccess = SetStdHandle(STD_OUTPUT_HANDLE, hWritePipe);
  PERR(bSuccess, "SetStdHandle");
  bSuccess = SetStdHandle(STD_ERROR_HANDLE, hWritePipe2);
  PERR(bSuccess, "SetStdHandle");
  bSuccess = DuplicateHandle(GetCurrentProcess(), /* source process */
      GetStdHandle(STD_INPUT_HANDLE), /* handle to duplicate */
      GetCurrentProcess(), /* destination process */
      NULL, /* new handle - don't want one, change original handle */
      0, /* new access flags - ignored since DUPLICATE_SAME_ACCESS */
      FALSE, /* it's *not* inheritable */
      DUPLICATE_SAME_ACCESS);
  PERR(bSuccess, "DuplicateHandle");
#endif

  /* Now create the child process, inheriting handles */

  bSuccess = CreateProcess(NULL,  /* filename */
      szArgs,  /* full command line for child */
      NULL,  /* process security descriptor */
      NULL,  /* thread security descriptor */
      TRUE,  /* inherit handles? Also use if STARTF_USESTDHANDLES */
      0,  /* creation flags */
      NULL,  /* inherited environment address */
      NULL,  /* startup dir; NULL = start in current */
      &si,  /* pointer to startup info (input) */
      &pi);  /* pointer to process info (output) */
  PERR(bSuccess, "CreateProcess");

  /* We can close the returned child process handle and thread
  handle as we won't be needing them; you could, however, wait on
  the process handle to wait until the child process terminates. */

  CloseHandle(pi.hThread);
  CloseHandle(pi.hProcess);

  /* We need to close our instances of the inheritable pipe write
  handle now that it's been inherited so that all open handles to
  the pipe are closed when the child process ends and closes its
  handles to the pipe. */

  bSuccess = CloseHandle(hWritePipe);
  PERR(bSuccess, "CloseHandle");
  bSuccess = CloseHandle(hWritePipe2);
  PERR(bSuccess, "CloseHandle");

  /* read from the pipe until we get an ERROR_BROKEN_PIPE */
  for (;;)
    {
    bSuccess = ReadFile(hReadPipe,  /* read handle */
        chReadBuffer,  /* buffer for incoming data */
        sizeof(chReadBuffer),  /* number of bytes to read */
        &cchReadBuffer,  /* number of bytes actually read */
        NULL);  /* no overlapped reading */
    if (!bSuccess && (GetLastError() == ERROR_BROKEN_PIPE))
        break;  /* child has died */

    PERR(bSuccess, "ReadFile");
    if (bSuccess && cchReadBuffer)
      {
      /* write the data from the child to the file */
      bSuccess = WriteFile(hOutFile,  /* write handle */
          chReadBuffer,  /* buffer to write */
          cchReadBuffer,  /* number of bytes to write */
          &cchReadBuffer,  /* number of bytes actually written */
          NULL);  /* no overlapped writing */
      PERR(bSuccess, "WriteFile");

      /* write buffer (of specified length) to console */
      printf("%.*s", cchReadBuffer, chReadBuffer);
      }
    }
  /* close the trace file, pipe handles */
  CloseHandle(hOutFile);
  CloseHandle(hReadPipe);
  return(0);
}

void PERR(BOOL bSuccess, char * api)
{
    DWORD       dwError;
    TCHAR       szMsgBuf[500];

    if (!(bSuccess))
    {
        dwError = GetLastError();
        printf("\nFile:%s\nError occured in %s on line %d\n", __FILE__, api, __LINE__);

    FormatMessage(
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,                               // ignored
        dwError,                     // message id
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),   // message language
        szMsgBuf,                   // address of buffer pointer
        500,                                  // minimum buffer size
        NULL );                              // no other arguments

        printf("\nError:(%ld)%s ", dwError, szMsgBuf);
     }
}


README.TXT

Nmpipe.c is a simple, console named pipe sample demonstrating the "thread per
client" method of a named pipe client/server architecture. This sample
application demonstrates how to create and connect clients from a server
application, and how to wait for and connect to pipes from a client
application.

To compile the application for simultaneous reading and writing, uncomment the
"#define OVERPAPED_IO TRUE" statement in nmpipe.c. For one-way pipe operations,
uncomment the "#define OVERLAPPED_IO FALSE" statement.

The sample can be started either as a server or as a client. To start the
sample as the server, use the /s parameter. To start the sample as a client,
use /c. Following either /s or /c should be a string to write to the pipe.
Strings with spaces should be quoted. Following the string is an optional
parameter that specifies the full name of the pipe to create or connect to. If
none is given, a default pipe name is used.

For example, to start a server and client:

nmpipe /s "hello from the server " \\.\pipe\testpipe
nmpipe /c client \\.\pipe\testpipe

If OVERLAPPED_IO is defined as TRUE, this will cause the client and server to
write their respective strings to each other over the named pipe. If
OVERLAPPED_IO is defined as FALSE, then the client will only write and the
server will only read.

Note that Windows 95 cannot create named pipes, so the /s (run as server) flag
will cause errors on the CreateNamedPipe call under Windows 95. In addition,
Windows 95 does not support overlapped I/O on named pipes; if the app is
compiled with OVERLAPPED_IO defined as TRUE, errors will result from the
CreateFile calls, which use the FILE_FLAG_OVERLAPPED flag in this case. So be
sure to define OVERLAPPED_IO as FALSE before compiling on Windows 95.


NMPIPE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <process.h>

/* Error checking macro. If bSuccess == TRUE, print error info */
#define PERR(bSuccess, szApi) {if (!(bSuccess)) printf("%s: Error %d from %s \
    on line %d\n", __FILE__, GetLastError(), szApi, __LINE__);}

#define DEFNAME "\\\\.\\pipe\\nmpipe" /* Default pipe name */
/* Define OVERLAPPED_IO to TRUE to use Overlapped IO. Otherwise define as
   FALSE */
//#define OVERLAPPED_IO TRUE
#define OVERLAPPED_IO FALSE

/* structure to pass into the writer thread */
typedef struct {
  HANDLE hPipe;     /* pipe write handle */
  char *szData;     /* data to repeatedly write */
  int iDataSize;    /* size of buffer pointed to by szData */
} WRITER_PARAMS;

/*
 ** BOOL pipeCheck(HANDLE h)
 *
 *  PARAMETERS: HANDLE hPipe: pipe handle to close if an error condition exists
 *
 *  DESCRIPTION: if GetLastError() returns a common error generated by a
 *               broken pipe, close the pipe handle. Call this right after a
 *               pipe operation.
 *
 *  RETURNS: FALSE if the pipe is broken, TRUE if not.
 *
 */
BOOL pipeCheck(HANDLE hPipe)
{
  DWORD dwLastError = GetLastError();

  if (dwLastError == ERROR_BROKEN_PIPE || dwLastError == ERROR_NO_DATA)
    {
    puts("\n* pipe broken, closing...");
    CloseHandle(hPipe);
    return(FALSE);
    }
  if (dwLastError == ERROR_INVALID_HANDLE) /* is handle already closed? */
    return(FALSE);
  return(TRUE);
} /* pipeCheck() */

/*
 ** void reader(HANDLE hPipe)
 *
 *  PARAMETERS: HANDLE hPipe: pipe handle to read from
 *
 *  DESCRIPTION: read from the handle and dump data to the console.
 *
 *  RETURNS: none
 *
 */
void reader(HANDLE hPipe)
{
  char buf[64];
  DWORD dwRead;
  BOOL bSuccess;
  OVERLAPPED ol;

  if (OVERLAPPED_IO) /* if overlapped, prepare OVERLAPPED structure */
  {
    memset(&ol, 0, sizeof(ol));
    ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    PERR(ol.hEvent, "CreateEvent");
  }
  while (1)
    {
    bSuccess = ReadFile(hPipe, buf, sizeof(buf), &dwRead,
        OVERLAPPED_IO ? &ol : NULL);
    if (!bSuccess && GetLastError() == ERROR_IO_PENDING)
      bSuccess = GetOverlappedResult(hPipe, &ol, &dwRead, TRUE);
    /* If ReadFile or GetOverlappedResult fails, check pipe. If pipe is
       broken, fall out of loop */
    if (!bSuccess && !pipeCheck(hPipe))
      break;
    /* else check for any other kinds of errors that may have occurred */
    PERR(bSuccess, "ReadFile");
    printf("%.*s", dwRead, buf); /* print only number of chars read */
    } /* while */
  CloseHandle(ol.hEvent);
  _endthread();
} /* reader() */

/*
 ** void writer(WRITER_PARAMS *writer_params)
 *
 *  PARAMETERS: WRITER_PARAMS *writer_params: misc info needed for writing to
 *              the pipe
 *
 *  DESCRIPTION: write data from WRITER_PARAMS to the pipe
 *
 *  RETURNS: none
 *
 */
void writer(WRITER_PARAMS *writer_params)
{
  DWORD dwWritten;
  BOOL bSuccess;
  OVERLAPPED ol;

  if (OVERLAPPED_IO)
  {
    memset(&ol, 0, sizeof(ol));
    ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    PERR(ol.hEvent, "CreateEvent");
  }
  while(1)
  {
    bSuccess = WriteFile(writer_params->hPipe, writer_params->szData,
        writer_params->iDataSize, &dwWritten, OVERLAPPED_IO ? &ol : NULL);
    if (!bSuccess && GetLastError() == ERROR_IO_PENDING)
      bSuccess = GetOverlappedResult(writer_params->hPipe, &ol, &dwWritten,
          TRUE);
    /* If ReadFile or GetOverlappedResult fails, check pipe. If pipe is
       broken, fall out of loop */
    if (!bSuccess && !pipeCheck(writer_params->hPipe))
      break;
    /* else check for any other kinds of errors that may have occurred */
    PERR(bSuccess, "WriteFile");
    } /* while */
  free(writer_params);
  CloseHandle(ol.hEvent);
  _endthread();
} /* writer() */

/*
 ** main()
 *
 *  DESCRIPTION: Connect pipe instances to clients. Start a reader thread for
 *               each client. If OVERLAPPED_IO is defined, also start a
 *               writer thread for full duplex pipe I/O testing. If this is
 *               a client, connect to the server pipe and start a writer
 *               thread. If OVERLAPPED_IO, also start a reader thread.
 *
 */

void main(int argc, char *argv[])
{
  HANDLE hPipe;
  BOOL bSuccess;
  BOOL bNotConnected;
  char *szPname;
  WRITER_PARAMS *writer_params;
  DWORD dwClients;
  SECURITY_ATTRIBUTES saPipe;
  OVERLAPPED ol;
  DWORD dwRead;

  if (argc < 3)
    {
    puts("nmpipe [/s|/c] <string> <pipename>");
    puts("'/s' to start as server, '/c' to start as client");
    puts("<string>: string to write to the pipe");
    puts("<pipename>: full UNC name of pipe (optional)");
    puts("example: nmpipe /s \"hello from server \" \\\\.\\pipe\\pipetst");
    return;
    }
  szPname = (argc > 3) ? argv[3] : DEFNAME;
  if (tolower(argv[1][1]) == 's')
    {
    if (OVERLAPPED_IO)
    {
      memset(&ol, 0, sizeof(ol));
      ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
      PERR(ol.hEvent, "CreateEvent");
    }
    SetConsoleTitle("SERVER: nmpipe sample");
    /* set up a NULL DACL in our pipe security descriptor to allow anyone to
       connect to the pipe server */
    saPipe.lpSecurityDescriptor =
        (PSECURITY_DESCRIPTOR) malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
    InitializeSecurityDescriptor(saPipe.lpSecurityDescriptor,
        SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(saPipe.lpSecurityDescriptor, TRUE, (PACL) NULL,
        FALSE);
    saPipe.nLength = sizeof(saPipe);
    saPipe.bInheritHandle = TRUE;
    while(1)
      {
      /* Create a named pipe: duplex, type byte, readmode byte, unlimited
         instances, default timeout of 60s */
      hPipe = CreateNamedPipe(szPname, PIPE_ACCESS_DUPLEX |
          (OVERLAPPED_IO ? FILE_FLAG_OVERLAPPED : 0), PIPE_TYPE_BYTE |
          PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0,
          60000, &saPipe);
      PERR(hPipe != INVALID_HANDLE_VALUE, "CreateNamedPipe");
      puts("\n* pipe created, waiting for connection...");
      bSuccess = ConnectNamedPipe(hPipe, OVERLAPPED_IO ? &ol : NULL);
      if (!bSuccess && GetLastError() == ERROR_IO_PENDING)
        bSuccess = GetOverlappedResult(hPipe, &ol, &dwRead, TRUE);
      /* check return from either ConnectNamedPipe or GetOverlappedResult.
         If a client managed to connect between the CreateNamedPipe and
         ConnectNamedPipe calls, ERROR_PIPE_CONNECTED will result */
      if (!bSuccess && GetLastError() != ERROR_PIPE_CONNECTED)
        {
        /* something went wrong, report error, close instance and try again */
        PERR(bSuccess, "ConnectNamedPipe");
        CloseHandle(hPipe);
        continue;
        }
      /* find out how many pipe instances there currently are */
      bSuccess = GetNamedPipeHandleState(hPipe, NULL, &dwClients, NULL, NULL,
          NULL, 0);
      PERR(bSuccess, "GetNamedPipeHandleState");
      printf("\n* %d clients connected", dwClients);
      _beginthread(reader, 0, hPipe);
      writer_params = malloc(sizeof(WRITER_PARAMS));
      writer_params->hPipe = hPipe;
      writer_params->szData = argv[2];
      writer_params->iDataSize = strlen(argv[2]);
      /* WARNING! reading and writing at the same time to a non-overlapped
         pipe is a no-no! If not overlapped, server only reads and client
         only writes */
      if (OVERLAPPED_IO)
        _beginthread(writer, 0, writer_params);
      } /* while */
    CloseHandle(ol.hEvent);
    CloseHandle(hPipe);
    }
  else /* no '/s', assume it's a client */
    {
    char szTemp[64];

    sprintf(szTemp, "Client %s", argv[2]);
    SetConsoleTitle(szTemp);
    bNotConnected = TRUE;
    while (bNotConnected)
      {
      /* attempt to connect to pipe instance */
      hPipe = CreateFile(szPname, GENERIC_READ | GENERIC_WRITE, 0, NULL,
          OPEN_EXISTING, OVERLAPPED_IO ? FILE_FLAG_OVERLAPPED : 0, NULL);
      if (GetLastError() == ERROR_PIPE_BUSY)
        {
        puts("Pipe busy, waiting for a pipe instance...");
        bSuccess = WaitNamedPipe(szPname, NMPWAIT_USE_DEFAULT_WAIT);
        PERR(bSuccess, "WaitNamedPipe");
        }
      else
        {
        PERR(hPipe != INVALID_HANDLE_VALUE, "CreateFile");
        bNotConnected = (hPipe == INVALID_HANDLE_VALUE);
        }
      } /* while */
    puts("Connected to pipe...");
    if (OVERLAPPED_IO) /* if overlapped, start reader thread */
      _beginthread(reader, 0, hPipe);
    writer_params = malloc(sizeof(WRITER_PARAMS));
    writer_params->hPipe = hPipe;
    writer_params->szData = argv[2];
    writer_params->iDataSize = strlen(argv[2]);
    writer(writer_params);
    } /* else */
  free(saPipe.lpSecurityDescriptor);
  CloseHandle(hPipe);
  CloseHandle(ol.hEvent);
  return;
}


README.TXT

Sample: Named Pipe Client/Server Demonstration

Summary:

NPCLIENT and NPSERVER demonstrate the use of named pipes.
The basic design consist of a server application serving
multiple client applications.  The user can use the client
applications as an interface to all of the other client
applications via the server.  The effect is a simple
communication program that can be used over the network
between multiple clients.

More Information:

The actual implementation works by having the NPSERVER
application launch a new thread, which creates and services
a new instance of the server side of the named pipe every
time a client connects to it.  You need only start one
instance of the NPSERVER application.  It will service up to
100 instances of the NPCLIENT application.  (Note that the
100 instance limit is hard coded into the sample.  It does
not reflect the number of named pipe instances you can
create, which is virtually infinite.)

TO USE:

Start an instance of NPSERVER.  A window will appear.

Start an instance of NPCLIENT.  Two dialog boxes will
appear, one on top of the other.  The top level dialog box
will prompt you for a share name and a client or user name.
If the instance of NPCLIENT is local to (on the same machine
as) the NPSERVER instance, enter a '.' for the share name.
Otherwise, enter the machine name of the server that the
NPSERVER instance was started on, i.e. 'FoobarServer'.  For
the client or user name, enter any name you wish to be
identified with.  Hit enter or click the OK button.

The upper dialog box will go away, and you'll see the Client
dialog box of NPCLIENT.  It consists of two edit fields and
a 'Send' button.  You will be able to read messages from
other clients (and yourself) in the larger/upper edit field.
(Note, if the message seems garbled, make sure the cursor of
the edit field is located in the lower left hand corner of
the field.)  The smaller edit field is used to type
messages.  To send a message: type something in the
lower/smaller edit field, and hit enter or click the Send
button.  The message will appear in the larger edit field of
all the clients connected to the NPSERVER instance;
prepended by the user name you selected.  Note that the user
name you selected will be entered into the caption bar of
the NPCLIENT instance.  This allows you to more easily keep
track of multiple instances of NPCLIENT on the same machine.

At the same time the top level dialog box was dismissed from
the NPCLIENT instance, the NPSERVER window was updated with
the picture of a red spool of thread accompanied by the user
name you selected.  This red spool indicates an active
client thread connected to NPSERVER.  The spool may be
connected to other spools with a thin blue line (similar to
the way the File Manager connects files or directories).
Any time a client disconnects from NPSERVER; the spool
representing it will be grayed out.

DESIGN:

Basically, the NPSERVER application launches multiple
instances of a server thread.  When the application is
started, the first thread is created.  It creates an
instance of the server side of the named pipe, and waits for
a client to connect.  Once a client connects, another thread
is started and it too blocks waiting for a client.
Meanwhile, the first thread updates a global array of client
information with this specific client's information.  The
thread then enters a loop reading from this client.  Any
time this specific client sends a message, this server
thread will call a function (TellAll) which will write the
message to all the clients that have been listed in the
global array.

On the client side, NPCLIENT tries to connect to the named
pipe with a CreateFile call.  Once it has connected, it
creates a thread which loops and reads any message from the
server side.  Once a message is read, it is printed in the
larger edit field.  Any time the user hits the Send button,
the main thread grabs any text in the lower edit field, and
writes it to the server.

The steps between NPSERVER and an instance of NPCLIENT looks
like this:

   NPSERVER                          NPCLIENT
   --------                          --------

   CreateNamedPipe()
   ConnectPipe()   // Blocks
                                      CreateFile()  //Connects to pipe.
                                      spawn separate thread to read pipe
   return from block
   updates array of clients
   spawn another server thread
   Loop
     ReadFile() // Blocks on overlap
                                      WriteFile() // User hits Send.

     return from block
     WriteFile() // Broadcast to clients
   End loop      // When client breaks pipe.

                                      ReadPipe Thread:
                                        Loop
                                          ReadFile()
                                            block till server broadcasts

                                            return from block.
                                            put string in edit field.

                                         End loop // when server breaks.

The overlapped structure should be used anytime a pipe is
expected to block for any length of time on a read or write.
This allows the thread to return immediately from a read or
write to service any other part of your application.  The
overlapped structure should also be used on a named pipe
anytime you expect to do simultaneous reads and writes.


CLIENT32.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// Various buffer sizes
#define IN_BUF_SIZE        1000
#define OUT_BUF_SIZE       1000
#define PLEASE_READ        1000
#define PLEASE_WRITE       1000
#define LINE_LEN           80
#define NAME_SIZE          25

// For the Client dialog box.
#define IDB_SEND           904
#define IDD_EDITWRITE      905
#define IDD_EDITREAD       906

// For the Init dialog box.
#define IDD_SVREDIT        701
#define IDB_INITOK         705
#define IDD_CLNTEDIT       704

#define WM_APP             0x8000
#define WM_INITCLIENT      WM_APP
#define WM_GO_AWAY         WM_APP+1

// Stringtable identifiers
#define IDS_CANTFINDPIPE    100
#define IDS_GENERALERROR    101
#define IDS_DEBUGTITLE      102
#define IDS_CONNECTBROKEN   103
#define IDS_READFAILED      104
#define IDS_CLIENTDBG       105

LONG CALLBACK MainWndProc   (HWND, UINT, WPARAM, LPARAM);
LONG CALLBACK ClientDlgProc (HWND, UINT, WPARAM, LPARAM);
LONG CALLBACK InitDlgProc   (HWND, UINT, WPARAM, LPARAM);
VOID          ReadPipe      (HANDLE *);


CLIENT32.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*************************************************************************\
*  PROGRAM: client32.c
*
*  PURPOSE:
*
*     To demonstrate the use of named pipes and the overlapped structure.
*     This code serves as the client side of the named pipe instances.
*     For more details on an overview of this codes designs or use, see
*     the README file.  For details on the implementation, see the comments
*     in this code.
*
*
\*************************************************************************/

#define  STRICT
#include <windows.h>
#include "client32.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

HANDLE hInst;
HWND   hWndClient;

CHAR ShrName[LINE_LEN];                 // Global: net share name.
CHAR ClntName[NAME_SIZE];               // Global: user or pipe client name.
CHAR lpBuffer[255];                     // Global: buffer for string resources

/*************************************************************************\
*
*  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
*
*  PURPOSE: Launches the Client's dialog box.
*
*  COMMENTS:
*
\*************************************************************************/

int APIENTRY WinMain (HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPSTR     lpCmdLine,
                      int       nCmdShow)

{
  DWORD retCode;

  UNREFERENCED_PARAMETER( nCmdShow );
  UNREFERENCED_PARAMETER( lpCmdLine );
  UNREFERENCED_PARAMETER( hPrevInstance );

  hInst   = hInstance;
  retCode = DialogBox ((HANDLE)hInst, (LPCTSTR)"ClientDialog",
                       NULL, (DLGPROC)ClientDlgProc);
  return  (retCode);

}

/*************************************************************************\
*
*  PROCEDURE: InitDlgProc (HWND hDlg, WORD wMsg, LONG wParam, LONG lParam)
*
*  PURPOSE:  This dialog box prompts the user for a net share name and
*            a client or user name.  These values are placed into global
*            strings ShrName and ClntName.
*
*  CALLED BY:
*
*    ClientDlgProc();
*
\*************************************************************************/

LONG CALLBACK InitDlgProc (HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  {

  UNREFERENCED_PARAMETER(lParam);

  switch (wMsg)
    {
    case WM_INITDIALOG:
      PostMessage (GetDlgItem (hDlg, IDD_SVREDIT),
                   EM_LIMITTEXT, LINE_LEN, 0);

      PostMessage (GetDlgItem (hDlg, IDD_CLNTEDIT),
                   EM_LIMITTEXT, NAME_SIZE, 0);
    case WM_COMMAND:
      switch (LOWORD(wParam))
        {                            // When the user clicks okay, get the
        case IDB_INITOK:             // share name and user name from the
                                     // edit fields.
          GetWindowText (GetDlgItem (hDlg, IDD_SVREDIT), ShrName, LINE_LEN);
          GetWindowText (GetDlgItem (hDlg, IDD_CLNTEDIT), ClntName, NAME_SIZE);
          EndDialog(hDlg, 0);
          return (0);

        default:
          return (0);
        }
    default:
      return (0);
    }
  return (0);
  }

/*************************************************************************\
*
*  PROCEDURE: ClientDlgProc (HWND hDlg, WORD wMsg, LONG wParam, LONG lParam)
*
*  PURPOSE:  This procedure services the dialog box that serves as an interface
*            to the named pipe server instance.  The larger edit field is used
*            to read messages from the server instance.  The smaller edit field
*            is used to type messages to the server instance.  This procedure
*            is responsible for connecting to the named pipe, creating a
*            seperate thread to read the pipe, and sending to the pipe.
*
*  CALLED BY:
*
*    WinMain();
*
*  CALLS TO:
*
*    InitDlgProc();
*    ReadPipe();
*
\**************************************************************************/

LONG CALLBACK ClientDlgProc (HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  {
  DWORD  retCode;                      // Return code.
  CHAR   errorBuf[LINE_LEN] = "";      // Error message buffer.
  CHAR   outBuf[OUT_BUF_SIZE]  = "";   // Buffer trapping message to send.
  CHAR   sendBuf[OUT_BUF_SIZE] = "";   // Buffer used to modify message.
  DWORD  bytesWritten;                 // Used for WriteFile().
  DWORD  threadID;                     // Used for CreateThread().
  CHAR   fileName[LINE_LEN+NAME_SIZE+2]; // Used to modify pipe/file name.
  DWORD  lastError;                    // Used to get returns from GetLastError.

  static HANDLE hPipe;                 // File or Pipe handle.
  static OVERLAPPED OverLapWrt;        // Overlapped structure
  static HANDLE     hEventWrt;         // Event handle for overlapped writes.

  UNREFERENCED_PARAMETER( lParam );

  hWndClient = hDlg;

  switch (wMsg)
    {

    case WM_COMMAND:
      switch (LOWORD(wParam))
        {

        // When the user presses Send: capture the string from the edit
        // field, prepend it with the user name, and overlap write it to
        // the server instance of the named pipe.

        case IDB_SEND:                 // Get the text from the edit field.
          GetWindowText (GetDlgItem(hDlg,IDD_EDITWRITE),
                         outBuf, PLEASE_WRITE);

                                       // Prepend it with the user name, and
                                       // terminate it with a new line
                                       // character.

          wsprintf (sendBuf, "%s%s %s\n", ClntName, ":", outBuf);

                                       // Do the overlapped write.
          retCode = WriteFile (hPipe, sendBuf, PLEASE_WRITE,
                     &bytesWritten, &OverLapWrt);
          if (!retCode)
            {
            lastError = GetLastError();
                                       // If Error = IO_PENDING, wait til
                                       // the event signals success.
            if (lastError == ERROR_IO_PENDING)
              WaitForSingleObject (hEventWrt, (DWORD)-1);
            }

          return (0);

        default:
          return (0);

        }

    case WM_INITCLIENT:

       // On initialization, use the Init dialog box prompt the user for a
       // net share name and a client or user name.  A share name of "."
       // means that the named pipe is local to this machine.  Named pipe
       // names should have the form '\\.\PIPE\<pipename>', for a local machine
       // or '\\<machinename>\PIPE\<pipename>' for remote machines.
       // Once the share name is captured from the Init dialog box,
       // convert the name into the proper form; then do a CreateFile()
       // to connect to the pipe.  Handle any error from the CreateFile().
       // Then write the user name to the server instance of the named
       // pipe.  Finally, create a thread to read the named pipe.

                                       // Launch Init dialog box to capture
                                       // share name and user name.
       DialogBox ((HANDLE)GetModuleHandle(NULL),
                  (LPCTSTR)"InitDialog",
                  (HWND)hDlg,
                  (DLGPROC)InitDlgProc);

                                       // Put captured user name in window
                                       // caption.
       SetWindowText (hDlg, ClntName);

                                       // Construct file/pipe name.
       wsprintf (fileName, "%s%s%s", "\\\\", ShrName, "\\PIPE\\test");

                                       // Do CreateFile() to connect to the
                                       // named pipe.
       hPipe = CreateFile (fileName,              // Pipe name.
                           GENERIC_WRITE          // Generic access, read/write.
                           | GENERIC_READ,
                           FILE_SHARE_READ        // Share both read and write.
                           | FILE_SHARE_WRITE ,
                           NULL,                  // No security.
                           OPEN_EXISTING,         // Fail if not existing.
                           FILE_FLAG_OVERLAPPED,  // Use overlap.
                           NULL);                 // No template.

                                       // Do some error checking.
       if ((DWORD)hPipe == 0xFFFFFFFF)
         {
         retCode = GetLastError();

                                       // This error means pipe wasn't found.
         if ((retCode == ERROR_SEEK_ON_DEVICE) ||
             (retCode == ERROR_FILE_NOT_FOUND)) {
             LoadString(hInst, IDS_CANTFINDPIPE, lpBuffer, sizeof(lpBuffer));
             MessageBox (hDlg, lpBuffer, "", MB_OK);
         }
         else
           {                           // Flagging unknown errors.
             LoadString(hInst, IDS_GENERALERROR, lpBuffer, sizeof(lpBuffer));
             wsprintf (errorBuf, lpBuffer, retCode);
             LoadString(hInst, IDS_DEBUGTITLE, lpBuffer, sizeof(lpBuffer));
             MessageBox (hDlg, errorBuf, lpBuffer,
                       MB_ICONINFORMATION | MB_OK | MB_APPLMODAL);
           }

         EndDialog (hDlg, 0);          // Kill app if pipe didn't connect.
         };

                                       // Create and init overlapped structure
                                       // for writes.
       hEventWrt = CreateEvent (NULL, TRUE, FALSE, NULL);
       OverLapWrt.hEvent = hEventWrt;

                                       // Write the client name to server.
       retCode = WriteFile (hPipe, ClntName, PLEASE_WRITE,
                            &bytesWritten, &OverLapWrt);

       if (!retCode)                   // Wait on overlapped if need be.
         {
         lastError = GetLastError();
         if (lastError == ERROR_IO_PENDING)
           WaitForSingleObject (hEventWrt, (DWORD)-1);
         }
                                       // Create a thread to read the pipe.
       CreateThread (NULL,
                     0,
                     (LPTHREAD_START_ROUTINE)ReadPipe,
                     (LPVOID)&hPipe,
                     0,
                     &threadID);
       return (0);

     case WM_INITDIALOG:
                                       // PostMessage() give time for the
                                       // dialog box to be created.
       PostMessage (hDlg, WM_INITCLIENT, 0, 0);
       return (0);

     case WM_GO_AWAY:
       CloseHandle (hPipe);
       CloseHandle (hEventWrt);
       EndDialog (hDlg, TRUE);
       return TRUE;

     case WM_SYSCOMMAND:
       if (wParam == SC_CLOSE)
         {
         CloseHandle (hPipe);
         CloseHandle (hEventWrt);
         EndDialog(hDlg, TRUE);
         return TRUE;
         }
       break;
     }
    return (FALSE);

  }

/*************************************************************************\
*
*  PROCEDURE: ReadPipe (HANDLE *hRead)
*
*  PURPOSE:  This is a thread function which loops and reads the named pipe.
*
*  CALLED BY:
*
*    ClientDlgProc.
*
*
\*************************************************************************/

VOID ReadPipe (HANDLE *hPipe)
  {
    CHAR       inBuf[IN_BUF_SIZE] = "";// Input buffer.
    DWORD      bytesRead;              // Used for ReadFile()
    DWORD      retCode;                // Used to trap return codes.
    CHAR       Buf[80];                // Message box buffer.
    DWORD      lastError;              // Used to trap returns from GetLastError.

    HANDLE     hEventRd;               // Event handle for overlapped reads.
    OVERLAPPED OverLapRd;              // Overlapped structure.
    DWORD      bytesTrans;             // Bytes transferred in read.

                                       // Create and init overlap structure.
    hEventRd = CreateEvent (NULL, TRUE, FALSE, NULL);
    memset (&OverLapRd, 0, sizeof(OVERLAPPED));
    OverLapRd.hEvent = hEventRd;

    // Loop, reading the named pipe until it is broken.  The ReadFile() uses
    // an overlapped structure.  When the event handle signals a completed
    // read, this loop writes the message to the larger edit field.

    do{
                                       // Read the pipe handle.
      retCode = ReadFile (*hPipe, inBuf, IN_BUF_SIZE, &bytesRead, &OverLapRd);

      if (!retCode) {                  // Do some error checking.

        lastError = GetLastError();
                                       // Check for 3 kinds of errors:
                                       // IO_PENDING, BROKEN_PIPE, or
                                       // other.
                                       // If Error = IO_PENDING, wait for
                                       // event handle to signal success.
        if (lastError == ERROR_IO_PENDING)
          {
          WaitForSingleObject (hEventRd, (DWORD)-1);

          }
        else {                         // If pipe is broken, tell user and break.

          if (lastError == (DWORD)ERROR_BROKEN_PIPE) {
            LoadString(hInst, IDS_CONNECTBROKEN, lpBuffer, sizeof(lpBuffer));
            MessageBox (hWndClient, lpBuffer, "", MB_OK);
          }
          else {                       // Or flag unknown errors, and break.
             LoadString(hInst, IDS_READFAILED, lpBuffer, sizeof(lpBuffer));
             wsprintf (Buf, lpBuffer, GetLastError());
             LoadString(hInst, IDS_CLIENTDBG, lpBuffer, sizeof(lpBuffer));
             MessageBox (hWndClient, Buf, lpBuffer, MB_OK);
          }
          break;
        }
      }
                                       // NULL terminate string.
      GetOverlappedResult (*hPipe, &OverLapRd, &bytesTrans, FALSE);
      inBuf[bytesTrans] = '\0';

                                       // Write message to larger edit field.
      SendMessage (GetDlgItem (hWndClient, IDD_EDITREAD),
                   EM_REPLACESEL,
                   0, (LONG)inBuf);
                                       // Add a new line.
      SendMessage (GetDlgItem (hWndClient, IDD_EDITREAD),
                   EM_REPLACESEL,
                   0, (LONG)"\r\n");
      }while(1);

                                       // When pipe is broken, send quit
                                       // messages to Client dialog box.
    PostMessage (hWndClient, WM_GO_AWAY, 0,0);
    ExitThread(0);
  }


SERVER32.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*************************************************************************\
*  PROGRAM: server32.c
*
*  PURPOSE:
*
*     To demonstrate the use of named pipes and the overlapped structure.
*     This code serves as the server side of the named pipe instances.
*     For more details on an overview of this codes designs or use, see
*     the README file.  For details on the implementation, see the comments
*     in this code.
*
*
\*************************************************************************/

#define  STRICT
#include <windows.h>
#include "server32.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

                                       // clients[] is a global array of
                                       // structures used to keep track
                                       // of the multiple instances of
                                       // the server side of the named
                                       // pipe.  As a client connects
                                       // to a given instance, a new
                                       // server thread is created and
                                       // added to the array.
WRTHANDLE clients[MAX_PIPE_INSTANCES];
DWORD     clientCount = 0;             // Global count of connected clients.

HWND   hWnd;
HANDLE hInst;
CHAR   lpBuffer[255];

/*************************************************************************\
*
*  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
*
*  PURPOSE: calls initialization function, processes message loop
*
*  COMMENTS:
*
\*************************************************************************/

int APIENTRY WinMain (HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPSTR  lpCmdLine,
                      int    nCmdShow)

{

  MSG  msg;
  WNDCLASS wc;

  UNREFERENCED_PARAMETER( lpCmdLine );
  UNREFERENCED_PARAMETER( hPrevInstance );

  //
  // Detect platform and exit gracefully if not Windows NT.
  //

  {
    OSVERSIONINFO osvi;
    osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);

    GetVersionEx (&osvi);
    if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
  LoadString(hInst, IDS_WRONGOS, lpBuffer, sizeof(lpBuffer));
      MessageBox (NULL, lpBuffer, "SERVER32", MB_OK | MB_ICONSTOP);
      return 0;
    }
  }

  hInst = hInstance;

  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)MainWndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon (hInstance, "npserver");
  wc.hCursor = LoadCursor (NULL, IDC_ARROW);
  wc.hbrBackground = GetStockObject (WHITE_BRUSH);
  wc.lpszMenuName = "PipeMenu";
  wc.lpszClassName = "PipeWClass";

  RegisterClass(&wc);

  LoadString(hInst, IDS_WINDOWTITLE, lpBuffer, sizeof(lpBuffer));
  hWnd = CreateWindow ("PipeWClass",
                       lpBuffer,
                       WS_OVERLAPPEDWINDOW,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       NULL,
                       NULL,
                       hInstance,
                       NULL);

  ShowWindow (hWnd, nCmdShow);

  while (GetMessage (&msg, NULL, 0, 0))
    DispatchMessage (&msg);

  return (msg.wParam);

}

/*************************************************************************\
*
*  FUNCTION:  MainWndProc (HWND, UINT, WPARAM, LPARAM)
*
*  PURPOSE:   To process messages.  To launch client and server threads
*
\*************************************************************************/

LONG CALLBACK MainWndProc (HWND   hwnd,
                           UINT   message,
                           WPARAM wParam,
                           LPARAM lParam)
{

  LONG        lpServerThreadID;
  PAINTSTRUCT paintStruct;
  HDC         hDC;

  switch (message)
      {
        case WM_PAINT:
           // DrawBranch is used to paint the spools and text to the window.
           hDC = BeginPaint (hwnd, &paintStruct);
           DrawBranch (hDC);
           EndPaint (hwnd, &paintStruct);
           return(0);

        case WM_CREATE :
           // Create the first instance of a server side of the pipe.
           CreateThread ((LPSECURITY_ATTRIBUTES)NULL,       // No security.
                         (DWORD)0,                          // Same stack size.
                         (LPTHREAD_START_ROUTINE)ServerProc,// Thread procedure.
                         (LPVOID)&hwnd,                     // Parameter.
                         (DWORD)0,                          // Start immediatly.
                         (LPDWORD)&lpServerThreadID);       // Thread ID.
           return (0);

        case WM_DESTROY :
           PostQuitMessage (0);
           return (0);
       }
    return DefWindowProc (hwnd, message, wParam, lParam);
}

/*************************************************************************\
*
*  PROCEDURE: ServerProc (HWND *hWnd)
*
*  PURPOSE:
*
*    A thread procedure, which creates an instance of the server side of
*    the named pipe, and then blocks waiting for a client to connect.
*    Once the client connects, a global array is updated with the specific
*    clients information, and this procedure is called again
*    to launch another waiting server thread.  After launching the new
*    thread, this thread begins to loop, reading the named pipe.  When
*    a message comes from it's client, it uses TellAll() to broadcast
*    the message to the other clients in the array.
*
*  CALLED BY:
*
*    ServerProc();
*    WinMain();
*
*  CALLS TO:
*
*    TellAll();
*    ServerProc().
*
*  COMMENTS:
*
*    Clients is a global array which hold information on each client
*    connected to the named pipe.  This procedure recieves a buffer.
*    It then steps through this global array, and for each client it
*    writes the buffer.
*
\*************************************************************************/

VOID ServerProc(HWND *hWnd)
 {
   HANDLE hPipe;                       // Pipe handle.
   CHAR   inBuf[IN_BUF_SIZE] = "";     // Input buffer for pipe.
   DWORD  ServerThreadID;              // Used for CreateThread().

   CHAR   errorBuf[LINE_LEN] = "";     // Used for error messages.
   DWORD  bytesRead;                   // Used in ReadFile().
   DWORD  retCode;                     // Used to trap return codes.
   DWORD  clientIndex;                 // Index into global array, for this
                                       // instances client.
   DWORD  lastError;                   // Traps returns from GetLastError().
   BOOL   ExitLoop = FALSE;            // Boolean Flag to exit loop.

   OVERLAPPED OverLapWrt;              // Overlapped structure for writing.
   HANDLE     hEventWrt;               // Event handle for overlapped write.

   OVERLAPPED OverLapRd;               // Overlapped structure for reading.
   HANDLE     hEventRd;                // Event handle for overlapped reads.
   DWORD        bytesTransRd;          // Bytes transferred by overlapped.
   PSECURITY_DESCRIPTOR    pSD;
   SECURITY_ATTRIBUTES     sa;

                                       // create a security NULL security
                                       // descriptor, one that allows anyone
                                       // to write to the pipe... WARNING
                                       // entering NULL as the last attribute
                                       // of the CreateNamedPipe() will
                                       // indicate that you wish all
                                       // clients connecting to it to have
                                       // all of the same security attributes
                                       // as the user that started the
                                       // pipe server.

   pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
               SECURITY_DESCRIPTOR_MIN_LENGTH);

   if (pSD == NULL)
     {
     MessageBox (*hWnd, "Error in LocalAlloc for pSD",
                 "Debug: ServerProc()", MB_OK);
     return;
     }

   if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
     {
     wsprintf (errorBuf, "Error: InitializeSecurityDescriptor() %d",
               GetLastError());
     MessageBox (*hWnd, errorBuf, "Debug: ServerProc()", MB_OK);
       LocalFree((HLOCAL)pSD);
       return;
     }

                                       // add a NULL disc. ACL to the
                                       // security descriptor.

   if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE))
     {
     wsprintf (errorBuf, "Error: SetSecurityDescriptorDacl() %d",
                GetLastError());
     MessageBox (*hWnd, errorBuf, "Debug: ServerProc()", MB_OK);
     LocalFree((HLOCAL)pSD);
     return;
     }

   sa.nLength = sizeof(sa);
   sa.lpSecurityDescriptor = pSD;
   sa.bInheritHandle = TRUE;

                                       // Create a local named pipe with
                                       // the name '\\.\PIPE\test'.  The
                                       // '.' signifies local pipe.
   hPipe = CreateNamedPipe ("\\\\.\\PIPE\\test", // Pipe name = 'test'.
               PIPE_ACCESS_DUPLEX                // 2 way pipe.
               | FILE_FLAG_OVERLAPPED,           // Use overlapped structure.
               PIPE_WAIT                         // Wait on messages.
               | PIPE_READMODE_MESSAGE           // Specify message mode pipe.
               | PIPE_TYPE_MESSAGE,
               MAX_PIPE_INSTANCES,               // Maximum instance limit.
               OUT_BUF_SIZE,                     // Buffer sizes.
               IN_BUF_SIZE,
               TIME_OUT,                         // Specify time out.
               &sa);                             // Security attributes.

                                       // Check Errors.
    if ((DWORD)hPipe == 0xFFFFFFFF)
     {

     retCode = GetLastError();         // Report any error, it should always succeed.
     LoadString(hInst, IDS_ERRORCODE, lpBuffer, sizeof(lpBuffer));
     wsprintf (errorBuf, lpBuffer, retCode);
     LoadString(hInst, IDS_DEBUGTITLE, lpBuffer, sizeof(lpBuffer));
     MessageBox (*hWnd, errorBuf, lpBuffer,
                 MB_ICONINFORMATION | MB_OK | MB_APPLMODAL);
     };

                                       // Block until a client connects.
   ConnectNamedPipe(hPipe, NULL);

                                       // Create and init overlap for writing.
   hEventWrt = CreateEvent (NULL, TRUE, FALSE, NULL);
   memset (&OverLapWrt, 0, sizeof(OVERLAPPED));
   OverLapWrt.hEvent = hEventWrt;

                                       // Set the clientIndex, then increment
                                       // the count.  Fill in the structure
                                       // for this client in the array.
   clientIndex = clientCount++;
   clients[clientIndex].hPipe   = hPipe;
   clients[clientIndex].live    = TRUE;
   clients[clientIndex].overLap = OverLapWrt;
   clients[clientIndex].hEvent  = hEventWrt;

                                       // Create and init overlap for reading.
   hEventRd = CreateEvent(NULL,TRUE,FALSE,NULL);
   memset (&OverLapRd, 0, sizeof(OVERLAPPED));
   OverLapRd.hEvent = hEventRd;

                                       // Read from the client, the first
                                       // first message should always be
                                       // the clients user name.
   retCode = ReadFile (hPipe, inBuf, PLEASE_READ, &bytesRead, &OverLapRd);

   if (!retCode)
    lastError = GetLastError();

   if (lastError == ERROR_IO_PENDING)  // Wait on read if need be.
     WaitForSingleObject (hEventRd, (DWORD)-1);

                                       // Put client's name in the array.
   strcpy (clients[clientIndex].Name, inBuf);

                                       // Create a thread which will make
                                       // another server instance of the
                                       // named pipe.
   CreateThread ((LPSECURITY_ATTRIBUTES)NULL,        // No security attributes.
                 (DWORD)0,                           // Use same stack size.
                 (LPTHREAD_START_ROUTINE)ServerProc, // Thread procedure.
                 (LPVOID)hWnd,                       // Parameter to pass.
                 (DWORD)0,                           // Run immediately.
                 (LPDWORD)&ServerThreadID);          // Thread identifier.

   TellAll("");                        // Forces a paint, draws a red spool
                                       // and name for this client.

                                       // Do loop which basically reads from
                                       // this specific client, and then
                                       // uses TellAll() to broadcast the
                                       // message to all the connected
                                       // clients.
   do{
                                       // Read the pipe.
      retCode = ReadFile (hPipe, inBuf, PLEASE_READ, &bytesRead, &OverLapRd);

                                       // Check for three kinds of errors:
                                       // If Error = IO_PENDING, wait til
                                       // the event handle signals success,
                                       // If BROKEN_PIPE, exit the do loop.
                                       // Any other error, flag it to the
                                       // user and exit the do loop.
      if (!retCode)
        {
        lastError = GetLastError();

        switch (lastError)
          {
                                       // IO_PENDING, wait on the event.
           case ERROR_IO_PENDING:
             WaitForSingleObject (hEventRd, (DWORD)-1);
             break;
                                       // Pipe is broken, exit the loop.
           case ERROR_BROKEN_PIPE:
             ExitLoop = TRUE;
             break;
                                       // Something else is wrong, exit the
                                       // the loop after telling the user.
           default:
             LoadString(hInst, IDS_READERROR, lpBuffer, sizeof(lpBuffer));
             wsprintf (errorBuf, lpBuffer, lastError);
             LoadString(hInst, IDS_DEBUGINFO, lpBuffer, sizeof(lpBuffer));
             MessageBox (*hWnd, errorBuf, lpBuffer, MB_OK);
             ExitLoop = TRUE;
             break;
          }
        }

      if (!ExitLoop)
        {
        GetOverlappedResult (hPipe, &OverLapRd, &bytesTransRd, FALSE);

                                       // Use TellAll to broadcast the message.
        if (bytesTransRd)
          TellAll(inBuf);
        else
          TellAll("");
        }

   }while(!ExitLoop);

   clients[clientIndex].live = FALSE;  // Turns spool gray.
   CloseHandle (hPipe);                // Close handles.
   CloseHandle (hEventRd);
   CloseHandle (hEventWrt);
   DisconnectNamedPipe (hPipe);        // Close pipe instance.
   ExitThread(0);                      // Clean up and die.
  }

/*************************************************************************\
*
*  PROCEDURE: TellAll (CHAR *buffer)
*
*  PURPOSE:
*
*    To write the buffer (input parameter) to all of the clients listed
*    in the global array "clients".
*
*  CALLED BY:
*
*    ServerProc();
*
*  COMMENTS:
*
*    Clients is a global array which hold information on each client
*    connected to the named pipe.  This procedure recieves a buffer.
*    It then steps through this global array, and for each client it
*    writes the buffer.
*
\*************************************************************************/

VOID TellAll( CHAR *buffer )
  {
    DWORD i;                           // Index through array.
    DWORD bytesWritten;                // Used in WriteFile().
    DWORD retCode;                     // Traps return codes.
    CHAR  Buf[LINE_LEN];               // Message Buffer.
    DWORD lastError;                   // Traps returns from GetLastError().

    for(i=0; i < clientCount; i++)     // For all clients in the array.
      {
                                       // If client isn't alive, don't waste
                                       // time writing to it.
      if (clients[i].live)
        {
        retCode = WriteFile (clients[i].hPipe, buffer, strlen(buffer),
                             &bytesWritten, &clients[i].overLap);

                                       // Check 3 kinds of errors: IO_PENDING,
                                       // NO_DATA, or other.  Wait on event
                                       // handle if IO_PENDING, else, if it's
                                       // anything other than NO_DATA (pipe
                                       // client disconnected), flag the user.
                                       // In any case, if it's not IO_PENDING,
                                       // clients[i].live = FALSE, spool turns
                                       // gray.
        if (!retCode)
          {
          lastError = GetLastError();

                                       // IO_PENDING, wait on event handle.
          if (lastError == ERROR_IO_PENDING)
            {
            WaitForSingleObject (clients[i].hEvent, (DWORD)-1);
            }
          else
            {
                                       // If not NO_DATA, flag user.
            if (lastError != ERROR_NO_DATA)
              {
              LoadString(hInst, IDS_DEBUGLAST, lpBuffer, sizeof(lpBuffer));
              wsprintf (Buf, "%s = %d", buffer, GetLastError());
              MessageBox(hWnd, Buf, lpBuffer, MB_OK);
              }
            clients[i].live = FALSE;
            }
          }
        } //if client.live
      } // for loop
                                       // Paint window with new information.
    InvalidateRect(hWnd, NULL, TRUE);
  }

/*************************************************************************\
*
*  PROCEDURE: DrawBranch (HDC hDC)
*
*  PURPOSE:
*
*    To draw one of four bitmaps for each client, depending upon the clients
*    status (alive = red spool, dead or disconnected = gray), and location in
*    the array.  It also draws the clients user name beside the spool.
*    This procedure is executed when the WM_PAINT message is trapped.
*
*  CALLED BY:
*
*    WinMain();
*
*  COMMENTS:
*
\*************************************************************************/
VOID DrawBranch(HDC hDC)
{
                                       // Spool bitmaps.
  HBITMAP hEndLive, hEndDead, hMidLive, hMidDead, hBitMap;

  HDC hDCMem;
  int X, Y;
  BITMAP bm;
  POINT ptSize, ptOrg;
  DWORD index;

                                       // Load bitmaps: two red (live),
                                       // two dead (gray).  End = end
                                       // of tree (last client to connect),
                                       // mid means in the middle somewhere.
       hEndLive = LoadBitmap (hInst, "EndLive");
       hEndDead = LoadBitmap (hInst, "EndDead");

       hMidLive = LoadBitmap (hInst, "MidLive");
       hMidDead = LoadBitmap (hInst, "MidDead");

                                       // For each client, determine if
                                       // if alive or not, and position;
                                       // then blt appropriate map and
                                       // clients name.
    for (index = 0; index < clientCount; index++)
      {

      if (index < clientCount - 1)     // ClientCount - 1 = last (end) client.
       {
        if(clients[index].live)        // If live = red, else = gray.
         hBitMap = hMidLive;
        else
         hBitMap = hMidDead;
       }
      else
       {
        if(clients[index].live)        // If live = red, else = gray.
         hBitMap = hEndLive;
        else
         hBitMap = hEndDead;
       }
                                       // Calculate coordinates:
      X = BITMAP_X;                    // X position is constant.
      Y = index * BITMAP_Y;            // Y is based on index in the array.

                                       // Blt the chosen map.
      hDCMem = CreateCompatibleDC(hDC);
      SelectObject(hDCMem, hBitMap);
      SetMapMode(hDCMem, GetMapMode(hDC));

      GetObject(hBitMap, sizeof(BITMAP), &bm);

      ptSize.x = bm.bmWidth;
      ptSize.y = bm.bmHeight;
      DPtoLP (hDC, &ptSize, 1);

      ptOrg.x = 0;
      ptOrg.y = 0;
      DPtoLP (hDCMem, &ptOrg, 1);

      BitBlt(hDC, X, Y, ptSize.x, ptSize.y,
             hDCMem, ptOrg.x, ptOrg.y, SRCCOPY);

      X =  NAME_X;                     // Relocate X,Y for clients name.
      Y += NAME_Y;
                                       // Write name next to spool.
      TextOut (hDC, X, Y, clients[index].Name, strlen(clients[index].Name));
      DeleteDC(hDCMem);
      }

}


SERVER32.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define MAX_PIPE_INSTANCES 100

#define LINE_LEN   80
#define NAME_SIZE  25

// Bitmaps and coordinates.
#define END_LIVE   1
#define END_DEAD   2
#define MID_LIVE   3
#define MID_DEAD   4
#define BITMAP_X   20
#define BITMAP_Y   32
#define NAME_X     60
#define NAME_Y     10

// Buffers and pipe symbolic constants.
#define IN_BUF_SIZE        1000
#define OUT_BUF_SIZE       1000
#define TIME_OUT           0
#define PLEASE_READ        1000
#define PLEASE_WRITE       1000

//Stringtable identifiers
#defineIDS_WRONGOS     100
#define IDS_WINDOWTITLE 101
#define IDS_ERRORCODE   102
#define IDS_DEBUGTITLE  103
#define IDS_READERROR   104
#define IDS_DEBUGINFO   105
#define IDS_DEBUGLAST   106

// Structure used to hold information about a pipe client.
// They're used as array elements.
typedef struct { HANDLE     hPipe;
                 HANDLE     hEvent;
                 OVERLAPPED overLap;
                 BOOL       live;
                 CHAR Name[NAME_SIZE];
               } WRTHANDLE;

LONG CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
VOID          ServerProc (HWND *);
VOID          TellAll    (CHAR *);
VOID          DrawBranch(HDC);


README.TXT

How to Share Memory Between Processes.

SUMMARY
=======

The MEMORY sample demonstrates the use of the file mapping functions to
create named shared memory.

MORE INFORMATION
================

This sample consists of a client and a server.

Server
------

The server sets up the named share memory. A server can be created by
selecting Server from the Create menu. This opens up a MDI child and swaps
the menu bar to the server menu bar. To set up the equivalent of a named
shared memory:

  - Select Create File from the Server menu. This brings up the File Type
    dialog box. The user may create either a memory page file or a physical
    file by clicking on the Page File or the Map File button respectively to
    back up the soon-to-be-created named shared memory. If the Map File
    button is selected, the named shared memory will be backed up by a
    physical file on the disk. On the other hand, if the Page File button
    is clicked, the memory will be backed up by the memory page file.

  - Select Create File Mapping from the Server menu. This brings up the Map
    Name dialog box. The user then specifies a name for the file-mapping
    object which will be created for the file created in step 1 above. This
    name will be used to identify the shared memory by the clients in the
    other processes.

  - Select Map View of File from the Server menu. This maps the file-mapping
    object created in the step above into the process virtual address space.

  - Select Access from the Server menu. This creates a MLE inside the MDI
    Server child. Whatever was written in the MLE is put in the map file
    object.

Client
------

The client connects to the named shared memory created by the server
process. A client can be created by selecting Client from the Create menu.
This opens up an MDI child and swaps the menu bar to the client menu bar. To
set up the connection to the named share memory:

  - Select Open File Mapping from the Client menu. This brings up the Map
    Name dialog box. The user can then enter the name of the file-mapping
    object which the client wanted to connect to.

  - Select Map View of File from the Client menu. This maps the file-mapping
    object opened in the step above into the process virtual address space.

  - Select Access from the Client menu. This creates a MLE inside the MDI
    Server child. Whatever was written in the file-mapping object by the
    server will be shown in this MLE. The client synchronizes with the
    server at regular intervals.

  - Select Refresh Now from the Client menu to refresh the contents of the
    file-mapping object immediately.


MEMORY.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/****************************** Module Header ******************************\
* Module Name: memory.c
\***************************************************************************/

#include <windows.h>
#include "memory.h"

HANDLE ghModule;
HWND   ghwndMain = NULL;
HWND   ghwndClient;

HMENU  hMenu,       hMenuWindow;
HMENU  hServerMenu, hServerMenuWindow;
HMENU  hClientMenu, hClientMenuWindow;

CHAR   gszFile[20];
CHAR   gszMapName[20];

typedef struct _PerWndInfo
{
    HWND    hParent;
    HWND    hThisWnd;           // Used in locating the node
    RECT    rcClient;
    char    CaptionBarText[SIZEOFCAPTIONTEXT];
} PERWNDINFO, *PPERWNDINFO;

typedef struct _node
{
   PERWNDINFO      ChildWnd;
   HANDLE          hNext;
} NODE, *PNODE;

/*
 * Forward declarations.
 */
BOOL InitializeApp   (void);
LONG APIENTRY MainWndProc     (HWND, UINT, DWORD, LONG);
LONG APIENTRY ServerWndProc   (HWND, UINT, DWORD, LONG);
LONG APIENTRY ClientWndProc   (HWND, UINT, DWORD, LONG);
BOOL CALLBACK About          (HWND, UINT, DWORD, LONG);
BOOL CALLBACK FileType       (HWND, UINT, DWORD, LONG);
BOOL CALLBACK MapFileName     (HWND, UINT, DWORD, LONG);
LONG APIENTRY TextWndProc     (HWND, UINT, DWORD, LONG);

/***************************************************************************\
* main
*
\***************************************************************************/

int WINAPI WinMain(
    HANDLE hInstance,
    HANDLE hPrevInstance,
    LPSTR lpCmdLine,
    int nShowCmd)
{
    MSG msg;

    // this will change to something more reasonable

    ghModule = GetModuleHandle(NULL);
    if (!InitializeApp())
    {
        MessageBox(ghwndMain,
                   GetStringRes(IDS_ERR_INITAPP_FAILED),
                   NULL, MB_OK);

        return 0;
    }

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if (hMenuWindow && IsWindow(hMenuWindow))
        DestroyMenu(hMenuWindow);
    if (hClientMenuWindow && IsWindow(hClientMenuWindow))
        DestroyMenu(hClientMenuWindow);
    if (hServerMenuWindow && IsWindow(hServerMenuWindow))
        DestroyMenu(hServerMenuWindow);
    if (hMenu && IsWindow(hMenu))
        DestroyMenu(hMenu);
    if (hClientMenu && IsWindow(hClientMenu))
        DestroyMenu(hClientMenu);
    if (hServerMenu && IsWindow(hServerMenu))
        DestroyMenu(hServerMenu);

    return 1;

    UNREFERENCED_PARAMETER(lpCmdLine);
    UNREFERENCED_PARAMETER(nShowCmd);
    UNREFERENCED_PARAMETER(hInstance);
    UNREFERENCED_PARAMETER(hPrevInstance);
}

/***************************************************************************\
* InitializeApp
*
\***************************************************************************/

BOOL InitializeApp(void)
{
    WNDCLASS wc;

    wc.style            = CS_OWNDC;
    wc.lpfnWndProc      = (WNDPROC)MainWndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = sizeof(LONG);
    wc.hInstance        = ghModule;
    wc.hIcon            = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)(COLOR_APPWORKSPACE);
    wc.lpszMenuName     = "MainMenu";
    wc.lpszClassName    = "MemoryClass";

    if (!RegisterClass(&wc))
        return FALSE;

    wc.style            = CS_OWNDC;
    wc.lpfnWndProc      = (WNDPROC)ServerWndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = 0;                      // LATER sizeof(LONG);
    wc.hInstance        = ghModule;
    wc.hIcon            = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)(COLOR_APPWORKSPACE);
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = "ServerClass";

    if (!RegisterClass(&wc))
        return FALSE;

    wc.style            = CS_OWNDC;
    wc.lpfnWndProc      = (WNDPROC)ClientWndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = 0;                      // LATER sizeof(LONG);
    wc.hInstance        = ghModule;
    wc.hIcon            = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)(COLOR_APPWORKSPACE);
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = "ClientClass";

    if (!RegisterClass(&wc))
        return FALSE;

    wc.style            = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc      = (WNDPROC)TextWndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = 0;
    wc.hInstance        = ghModule;
    wc.hIcon            = NULL;
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)(COLOR_BTNSHADOW);
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = "Text";

    if (!RegisterClass(&wc))
            return FALSE;

    hMenu       = LoadMenu(ghModule, "MainMenu");
    hServerMenu = LoadMenu(ghModule, "ServerMenu");
    hClientMenu = LoadMenu(ghModule, "ClientMenu");
    hMenuWindow       = GetSubMenu(hMenu, 1);
    hServerMenuWindow = GetSubMenu(hServerMenu, 2);
    hClientMenuWindow = GetSubMenu(hClientMenu, 2);

    ghwndMain = CreateWindowEx(0L, "MemoryClass", "Memory",
            WS_OVERLAPPED   | WS_CAPTION     | WS_BORDER       |
            WS_THICKFRAME   | WS_MAXIMIZEBOX | WS_MINIMIZEBOX  |
            WS_CLIPCHILDREN | WS_VISIBLE     | WS_SYSMENU,
            80, 70, 592, 300,
            NULL, hMenu, ghModule, NULL);

    if (ghwndMain == NULL)
        return FALSE;

    SetWindowLong(ghwndMain, GWL_USERDATA, 0L);

    return TRUE;
}

/***************************************************************************\
* MainWndProc
*
\***************************************************************************/

long APIENTRY MainWndProc(
                          HWND hwnd,
                          UINT message,
                          DWORD wParam,
                          LONG lParam)
{
    static int         iSvrCount=1;
    static int         iCltCount=1;
    CLIENTCREATESTRUCT clientcreate;
    HWND               hwndChildWindow;
    BOOL               fSuccess;

    switch (message)
    {

      case WM_CREATE:
        SetWindowLong(hwnd, 0, (LONG)NULL);

        clientcreate.hWindowMenu  = hMenuWindow;
        clientcreate.idFirstChild = 1;

        ghwndClient = CreateWindow("MDICLIENT", NULL,
                                    WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
                                    0,0,0,0,
                                    hwnd, NULL, ghModule, (LPVOID)&clientcreate);
        return 0L;

      case WM_DESTROY: {
        PostQuitMessage(0);
        return 0L;
      }

      case WM_COMMAND:

        switch (LOWORD(wParam))
        {
            case IDM_TILE:
                SendMessage(ghwndClient, WM_MDITILE, 0L, 0L);
                return 0L;
            case IDM_CASCADE:
                SendMessage(ghwndClient, WM_MDICASCADE, 0L, 0L);
                return 0L;
            case IDM_ARRANGE:
                SendMessage(ghwndClient, WM_MDIICONARRANGE, 0L, 0L);
                return 0L;

            case MM_SERVER: {
                BOOL fSucess = FALSE;
                HANDLE hNode, hHead;
                PNODE  pNode;
                MDICREATESTRUCT mdicreate;

                hNode = LocalAlloc(LHND, (WORD) sizeof(NODE));
                if (hNode && (pNode = (PNODE)LocalLock(hNode)))
                {
                    wsprintf((LPSTR) &(pNode->ChildWnd.CaptionBarText),
                             GetStringRes(IDS_SERVER), iSvrCount);

                    mdicreate.szClass = "ServerClass";
                    mdicreate.szTitle = (LPTSTR)&(pNode->ChildWnd.CaptionBarText);
                    mdicreate.hOwner  = ghModule;
                    mdicreate.x       =
                    mdicreate.y       =
                    mdicreate.cx      =
                    mdicreate.cy      = CW_USEDEFAULT;
                    mdicreate.style   = 0l;
                    mdicreate.lParam  = 0L;

                    /*Create Child Window*/
                    hwndChildWindow =
                       (HWND) SendMessage(ghwndClient, WM_MDICREATE,
                                          0L,
                                          (LONG)(LPMDICREATESTRUCT)&mdicreate);

                    if (hwndChildWindow)
                    {
                       pNode->ChildWnd.hParent      = ghwndClient;
                       pNode->ChildWnd.hThisWnd     = hwndChildWindow;
                       hHead = (HANDLE)GetWindowLong(hwnd, 0);
                       pNode->hNext = hHead;
                       SetWindowLong(hwnd, 0, (LONG) hNode);

                       iSvrCount++;
                       LocalUnlock(hNode);

                       fSuccess = TRUE;
                    }
                    else
                    {
                       LocalUnlock (hNode);
                       LocalFree (hNode);

                       fSuccess = FALSE;
                    }
                }
                if (!fSuccess)
                    MessageBox(ghwndMain,
                               GetStringRes(IDS_ERR_CREATE_CHILD_FAILED),
                               NULL, MB_OK);

                 return 0L;
            }

            case MM_CLIENT:
            {
                BOOL fResult = FALSE;
                HANDLE hNode, hHead;
                PNODE  pNode;
                MDICREATESTRUCT mdicreate;

                hNode = LocalAlloc(LHND, (WORD) sizeof(NODE));
                if (hNode && (pNode = (PNODE)LocalLock(hNode)))
                {
                    wsprintf((LPSTR) &(pNode->ChildWnd.CaptionBarText),
                             GetStringRes(IDS_CLIENT), iCltCount);

                    mdicreate.szClass = "ClientClass";
                    mdicreate.szTitle = (LPSTR) &(pNode->ChildWnd.CaptionBarText);
                    mdicreate.hOwner  = ghModule;
                    mdicreate.x       =
                    mdicreate.y       =
                    mdicreate.cx      =
                    mdicreate.cy      = CW_USEDEFAULT;
                    mdicreate.style   = 0l;
                    mdicreate.lParam  = 0L;

                    /*Create Child Window*/
                    hwndChildWindow =
                        (HANDLE) SendMessage(ghwndClient, WM_MDICREATE,
                                             0L,
                                             (LONG)(LPMDICREATESTRUCT)&mdicreate);

                    if (hwndChildWindow)
                    {
                       pNode->ChildWnd.hParent      = ghwndClient;
                       pNode->ChildWnd.hThisWnd     = hwndChildWindow;
                       hHead = (HANDLE)GetWindowLong(hwnd, 0);
                       pNode->hNext = hHead;
                       SetWindowLong(hwnd, 0, (LONG) hNode);

                       iCltCount++;
                       LocalUnlock(hNode);

                       fSuccess = TRUE;
                    }
                    else
                    {
                       LocalUnlock (hNode);
                       LocalFree (hNode);

                       fSuccess = FALSE;
                    }
                }
                if (!fSuccess)
                   MessageBox(ghwndMain,
                              GetStringRes(IDS_ERR_CREATE_CHILD_FAILED),
                              NULL, MB_OK);

                return 0L;
            }

            case MM_ABOUT:
                if (DialogBox(ghModule, "AboutBox", ghwndMain, (DLGPROC)About) == -1)
                   MessageBox(ghwndMain,
                              GetStringRes(IDS_ERR_CREATE_DLG_FAILED), NULL, MB_OK);
                return 0L;

            case MM_OPT_1:
            case MM_OPT_2:
            case MM_OPT_3:
            case MM_OPT_4:
            case MM_OPT_5:
            case MM_OPT_6:
            case MM_OPT_7:
            case MM_OPT_8:
            {
                HWND hActiveChild;

                hActiveChild = (HANDLE) SendMessage(ghwndClient, WM_MDIGETACTIVE, 0L, 0L);
                if (hActiveChild)
                    SendMessage(hActiveChild, WM_COMMAND, wParam, lParam);
                return 0L;
            }

            default:
                return DefFrameProc(hwnd,  ghwndClient, message, wParam, lParam);
        }

    default:

        return DefFrameProc(hwnd,  ghwndClient, message, wParam, lParam);
    }
}

/***************************************************************************\
* ServerWndProc
*
\***************************************************************************/

long APIENTRY ServerWndProc( HWND hwnd,
                             UINT message,
                             DWORD wParam,
                             LONG lParam)
{
   static HANDLE MapFileHandle = NULL;
   static HANDLE hMem1         = NULL;
   static LPVOID pShrMem1      = NULL;
   static HANDLE hEdit;
   static BOOL   bDirty        = FALSE;
   static HWND   hTextWnd;
   BOOL          fSuccess;

   switch (message)
   {
      case WM_COMMAND:
      {

         switch (LOWORD(wParam))
         {
            case MM_OPT_1:
            { //Create File

               SetWindowText(hTextWnd, GetStringRes(IDS_CREATE_MAP_FILE));

               switch (DialogBox(ghModule, "FileType", hwnd, (DLGPROC)FileType))
               {
                  case -1:
                     fSuccess = FALSE;
                     break;

                  case IDBTN_MAP:
                     if (MapFileHandle = CreateMapFile(gszFile))
                        fSuccess = TRUE;
                     else
                        fSuccess = FALSE;

                     break;

                  default:
                     MapFileHandle = (HANDLE) 0xFFFFFFFF;
                     fSuccess = TRUE;
                     break;
               }

               if (fSuccess)
               {
                  EnableMenuItem(hServerMenu, MM_OPT_1, MF_GRAYED);
                  EnableMenuItem(hServerMenu, MM_OPT_2, MF_ENABLED);

                  SetWindowText(hTextWnd,
                                GetStringRes(IDS_SEL_CREATE_FILE_MAPPING));
               }
               else
               {
                  SetWindowText(hTextWnd,
                                GetStringRes (IDS_ERR_MAPFILE_FAILED));

               }
               return 0L;
            }

            case MM_OPT_2:
            { //Create File Mapping

               // This option should be disabled until MM_OPT_1 has been chosen.

               SetWindowText(hTextWnd,
                             GetStringRes(IDS_CREATE_MAPPING));

               if (MapFileHandle)
                  switch (DialogBox(ghModule, "MapName", hwnd, (DLGPROC)MapFileName))
                  {
                     case IDBTN_OK:
                        if (hMem1 = CreateMap(MapFileHandle, gszMapName))
                           fSuccess = TRUE;
                        else
                           fSuccess = FALSE;
                        break;

                     default:
                        fSuccess = FALSE;
                        break;
                  }
               else
                  fSuccess = FALSE;

               if (fSuccess)
               {
                  EnableMenuItem(hServerMenu, MM_OPT_2, MF_GRAYED);
                  EnableMenuItem(hServerMenu, MM_OPT_3, MF_ENABLED);

                  SetWindowText(hTextWnd,
                                GetStringRes (IDS_SEL_MAP_VIEW));
               }
               else
               {
                  SetWindowText(hTextWnd,
                                GetStringRes (IDS_ERR_MAPPING_FAILED));

               }
               return 0L;
            }

            case MM_OPT_3:
            { //Map View of File

               SetWindowText(hTextWnd, GetStringRes (IDS_MAPPING_VIEW));

               if (hMem1 && (pShrMem1 = (LPVOID)MapView(hMem1)))
               {
                  EnableMenuItem(hServerMenu, MM_OPT_3, MF_GRAYED);
                  EnableMenuItem(hServerMenu, MM_OPT_4, MF_ENABLED);

                  SetWindowText(hTextWnd, GetStringRes (IDS_SEL_ACCESS));

               }
               else
               {
                  SetWindowText(hTextWnd, GetStringRes(IDS_ERR_MAPVIEW_FAILED));
               }
               return 0L;
            }

            case MM_OPT_4:
            { //Access
               RECT    rcl;

               SetWindowText(hTextWnd,
                             GetStringRes (IDS_ACCESSING_SERVER_WRITE));

               if (pShrMem1)
               {
                  GetClientRect(hwnd, &rcl);
                  hEdit = CreateWindow("edit", (LPSTR) NULL,
                                        WS_CHILD      | WS_VISIBLE     |
                                        WS_HSCROLL    | WS_VSCROLL     |
                                        WS_BORDER     | ES_LEFT |
                                        ES_MULTILINE  | ES_AUTOHSCROLL |
                                        ES_AUTOVSCROLL,
                                        0,0, rcl.right - rcl.left,
                                        rcl.bottom - rcl.top -
                                        GetWindowLong(hTextWnd, GWL_USERDATA),
                                        hwnd, (HMENU)1, ghModule, (LPVOID)NULL);
                  if (hEdit)
                  {
                     EnableMenuItem(hServerMenu, MM_OPT_4, MF_GRAYED);
                     SetFocus(hEdit);
                     fSuccess = TRUE;
                  }
                  else
                     fSuccess = FALSE;
               }
               else
                  fSuccess = FALSE;

               if (!fSuccess)
               {
                  MessageBox(ghwndMain,
                             GetStringRes(IDS_ERR_ACCESS_WRITE_FAILED),
                             NULL, MB_OK);
                  SetWindowText(hTextWnd,
                                GetStringRes (IDS_ERR_ACCESS_WRITE_FAILED));
               }

               return 0L;
            }
         } // End of switch (LOWORD(wParam))

         switch (HIWORD(wParam))
         {
            case EN_UPDATE:
            {
               if (hEdit && (hEdit == (HWND)lParam))
               {
                  bDirty = TRUE;
               }
               return 0L;
            }
         }

      }
      case WM_TIMER:
         if (bDirty && IsWindow(hEdit))
         {
            int     iCnt;
            iCnt = SendMessage(hEdit, WM_GETTEXT, (WPARAM)4000, (LPARAM)pShrMem1);

            if (iCnt)
            {
               bDirty = FALSE;
            }

         }
         return 0L;

      case WM_MDIACTIVATE:
         if ((HWND) lParam == hwnd)
         {
            SendMessage(GetParent(hwnd), WM_MDISETMENU,
                        (DWORD)  hServerMenu,
                        (LONG)   hServerMenuWindow) ;
            DrawMenuBar(GetParent(GetParent(hwnd))) ;
         }
         return 0;

      case WM_SIZE:
         if (hEdit)
            MoveWindow(hEdit, 0, 0,
                       LOWORD(lParam),
                       HIWORD(lParam)-GetWindowLong(hTextWnd, GWL_USERDATA),
                       TRUE);

         MoveWindow(hTextWnd, 0,
                    HIWORD(lParam) - GetWindowLong(hTextWnd, GWL_USERDATA),
                    LOWORD(lParam),
                    HIWORD(lParam), TRUE);

         return DefMDIChildProc(hwnd, message, wParam, lParam);

      case WM_CREATE:
      {
         PPERWNDINFO      pWndInfo;
         PNODE            pHead;
         HANDLE           hHead, hTmp;
         RECT             rect;

         GetClientRect(hwnd, &rect);
         hTextWnd = CreateWindow("Text", NULL,
                                 WS_BORDER | SS_LEFT | WS_CHILD | WS_VISIBLE,
                                 0, 0, 0, 0,
                                 hwnd,
                                 (HMENU) 2,
                                 ghModule,
                                 NULL);

         EnableMenuItem(hServerMenu, MM_OPT_1, MF_ENABLED);

         SetWindowText(hTextWnd, GetStringRes (IDS_SEL_CREATE_FILE));

         // now find match
         hHead = (HANDLE) GetWindowLong(ghwndMain, 0);
         if (hHead && (pHead = (PNODE)LocalLock(hHead)))
         {
            while ((pHead->ChildWnd.hThisWnd != hwnd) && (pHead->hNext))
            {
               hTmp = hHead;
               hHead = pHead->hNext;
               LocalUnlock(hTmp);

               pHead = (PNODE)LocalLock(hHead);
            }

            if (pHead->ChildWnd.hThisWnd == hwnd)
            {
               pWndInfo = &pHead->ChildWnd;
               GetClientRect(pWndInfo->hThisWnd, &pWndInfo->rcClient);
            }

            LocalUnlock(hHead);
         }
         return DefMDIChildProc(hwnd, message, wParam, lParam);
      }

      case WM_CLOSE:
      {
         PPERWNDINFO      pWndInfo;
         PNODE            pHead, pTrail;
         HANDLE           hHead, hTmp;

         EnableMenuItem(hServerMenu, MM_OPT_2, MF_GRAYED);
         EnableMenuItem(hServerMenu, MM_OPT_3, MF_GRAYED);
         EnableMenuItem(hServerMenu, MM_OPT_4, MF_GRAYED);

         SendMessage(GetParent(hwnd), WM_MDISETMENU, (DWORD)hMenu, (LONG)hMenuWindow);
         DrawMenuBar(GetParent(GetParent(hwnd))) ;

         // Unmap view, close mapping and file!
         if (pShrMem1)
         {
            UnmapViewOfFile (pShrMem1);
            pShrMem1 = NULL;
         }

         if (hMem1)
         {
            CloseHandle (hMem1);
            hMem1 = NULL;
         }

         if (MapFileHandle && MapFileHandle != INVALID_HANDLE_VALUE)
         {
            CloseHandle (MapFileHandle);
            MapFileHandle = NULL;
         }

         // now find match
         hHead = (HANDLE) GetWindowLong(ghwndMain, 0);
         if ((hHead) && (pHead = (PNODE)LocalLock(hHead)))
         {
            pTrail = pHead;
            while ((pHead->ChildWnd.hThisWnd != hwnd) && (pHead->hNext))
            {
               hTmp = hHead;
               pTrail = pHead;
               hHead = pHead->hNext;
               LocalUnlock(hTmp);

               pHead = (PNODE) LocalLock(hHead);
            }

            if (pHead->ChildWnd.hThisWnd == hwnd)
            {
               pWndInfo = &pHead->ChildWnd;

               if (pTrail == pHead)
                  SetWindowLong(ghwndMain, 0, (LONG) pHead->hNext);
               else
                  pTrail->hNext = pHead->hNext;

               LocalUnlock(hHead);
               LocalFree(hHead);
            }
            else
            {
               LocalUnlock(hHead);
            }
         }
         return DefMDIChildProc(hwnd, message, wParam, lParam);
      }

      case WM_DESTROY:
         KillTimer(hwnd, 1);
         return 0L;

      case WM_PAINT:
         return DefMDIChildProc(hwnd, message, wParam, lParam);

      default:
         return DefMDIChildProc(hwnd, message, wParam, lParam);
   }
}

/***************************************************************************\
* ClientWndProc
*
\***************************************************************************/

long APIENTRY ClientWndProc( HWND hwnd,
                             UINT message,
                             DWORD wParam,
                             LONG lParam)
{
    static HANDLE hMem1    = NULL;
    static LPVOID pShrMem1 = NULL;
    static HANDLE hEdit;
    static HANDLE hTextWnd;
    BOOL   fSuccess = FALSE;

    switch (message)
    {
        case WM_COMMAND:
        {

          switch (LOWORD(wParam))
          {
             case MM_OPT_5:
             { //Open File Mapping

                 SetWindowText(hTextWnd, GetStringRes (IDS_OPENING_FILE));

                 switch (DialogBox(ghModule, "MapName", hwnd, (DLGPROC)MapFileName))
                 {
                     case IDBTN_OK:
                         if (hMem1 = OpenMap(gszMapName))
                             fSuccess = TRUE;
                         else
                            fSuccess = FALSE;
                         break;

                     default:
                         fSuccess = FALSE;
                         break;
                 }

                 if (fSuccess)
                 {
                    EnableMenuItem(hClientMenu, MM_OPT_5, MF_GRAYED);
                    EnableMenuItem(hClientMenu, MM_OPT_6, MF_ENABLED);

                    SetWindowText(hTextWnd, GetStringRes (IDS_SEL_MAP_VIEW));
                 }
                 else
                 {
                    SetWindowText(hTextWnd,
                                  GetStringRes (IDS_ERR_OPEN_MAPPING_FAILED));
                 }
                 return 0L;

            }
            case MM_OPT_6:
            { //Map View of File

                SetWindowText(hTextWnd, GetStringRes (IDS_MAPPING_VIEW));

                if (hMem1 && (pShrMem1 = (LPVOID) MapView(hMem1)))
                {
                   EnableMenuItem(hClientMenu, MM_OPT_6, MF_GRAYED);
                   EnableMenuItem(hClientMenu, MM_OPT_7, MF_ENABLED);

                   SetWindowText(hTextWnd, GetStringRes (IDS_SEL_ACCESS_READ));
                }
                else
                {
                   SetWindowText(hTextWnd,
                                 GetStringRes(IDS_ERR_MAPVIEW_FAILED));
                }

                return 0L;
            }

            case MM_OPT_7:
            { //Access
                RECT    rcl;

                 SetWindowText(hTextWnd,
                               GetStringRes(IDS_ACCESSING_SERVER_READ));

                 fSuccess = FALSE;  // assume failure.

                 if (pShrMem1)
                 {
                    GetClientRect(hwnd, &rcl);

                    hEdit = CreateWindow("edit", NULL,
                                   WS_CHILD     | WS_VISIBLE     |
                                   WS_HSCROLL   | WS_VSCROLL     |
                                   WS_BORDER    | ES_LEFT        |
                                   ES_MULTILINE | ES_AUTOHSCROLL |
                                   ES_READONLY  | ES_AUTOVSCROLL,
                                   0,0, rcl.right-rcl.left,
                                   rcl.bottom-rcl.top-GetWindowLong(hTextWnd, GWL_USERDATA),
                                   hwnd, (HMENU) 1, ghModule, NULL);
                    if (hEdit)
                    {
                       SetFocus(hEdit);
                       fSuccess = TRUE;
                    }
                 }

                 if (fSuccess)
                 {
                    SendMessage(hEdit, WM_SETTEXT, 0L, (LONG)pShrMem1);
                    SetTimer(hwnd, 2, 1000, NULL);
                    EnableMenuItem(hClientMenu, MM_OPT_7, MF_GRAYED);
                    EnableMenuItem(hClientMenu, MM_OPT_8, MF_ENABLED);
                 }
                 else
                 {
                    MessageBox(ghwndMain,
                               GetStringRes (IDS_ERR_ACCESS_READ_FAILED),
                               NULL, MB_OK);
                    SetWindowText(hTextWnd,
                                  GetStringRes (IDS_ERR_ACCESS_READ_FAILED));
                 }

             return 0L;
             }

            case MM_OPT_8:
            { // refresh now
               HANDLE hActive;

               hActive = (HANDLE) SendMessage(GetParent(hwnd), WM_MDIGETACTIVE, 0L, 0L);
               SendMessage(hEdit, WM_SETTEXT, 0L, (LONG)pShrMem1);
               return 0L;
            }
        }
    }

    case WM_TIMER:
    {
        HANDLE hActive;

        if (IsWindow (hEdit) && pShrMem1)
        {
           hActive = (HANDLE) SendMessage(GetParent(hwnd), WM_MDIGETACTIVE, 0L, 0L);
           SendMessage(hEdit, WM_SETTEXT, 0L, (LONG)pShrMem1);

}
        return 0L;
    }

    case WM_MDIACTIVATE:
        if ((HWND) lParam == hwnd)
        {
           SendMessage(GetParent(hwnd), WM_MDISETMENU,
                       (DWORD)  hClientMenu,
                       (LONG)   hClientMenuWindow);
           DrawMenuBar(GetParent(GetParent(hwnd))) ;
        }
        return 0;

    case WM_SIZE:
        if (hEdit)
           MoveWindow(hEdit, 0, 0,
                      LOWORD(lParam),
                      HIWORD(lParam)-GetWindowLong(hTextWnd, GWL_USERDATA),
                      TRUE);

        MoveWindow(hTextWnd, 0,
                   HIWORD(lParam) - GetWindowLong(hTextWnd, GWL_USERDATA),
                   LOWORD(lParam),
                   HIWORD(lParam), TRUE);

        return DefMDIChildProc(hwnd, message, wParam, lParam);

    case WM_CREATE:
    {
        PPERWNDINFO      pWndInfo;
        PNODE            pHead;
        HANDLE           hHead, hTmp;
        RECT             rect;

        GetClientRect(hwnd, &rect);
        hTextWnd = CreateWindow("Text", NULL,
                                WS_BORDER | SS_LEFT | WS_CHILD | WS_VISIBLE,
                                0, 0, 0, 0,
                                hwnd,
                                (HMENU) 2,
                                ghModule,
                                NULL);

        EnableMenuItem(hClientMenu, MM_OPT_5, MF_ENABLED);

        SetWindowText(hTextWnd, GetStringRes (IDS_SEL_OPEN_FILE));

        // now find match
        hHead = (HANDLE) GetWindowLong(ghwndMain, 0);
        if (hHead && (pHead = (PNODE)LocalLock(hHead)))
        {

           while ((pHead->ChildWnd.hThisWnd != hwnd) && (pHead->hNext))
           {
              hTmp = hHead;
              hHead = pHead->hNext;
              LocalUnlock(hTmp);
              pHead = (PNODE) LocalLock(hHead);
           }

           if (pHead->ChildWnd.hThisWnd == hwnd)
           {
              pWndInfo = &pHead->ChildWnd;
              GetClientRect(pWndInfo->hThisWnd, &pWndInfo->rcClient);
           }

           LocalUnlock(hHead);
           return DefMDIChildProc(hwnd, message, wParam, lParam);
        }
        return DefMDIChildProc(hwnd, message, wParam, lParam);
    }

    case WM_CLOSE:
    {
        PPERWNDINFO      pWndInfo;
        PNODE            pHead, pTrail;
        HANDLE           hHead, hTmp;

        EnableMenuItem(hClientMenu, MM_OPT_6, MF_GRAYED);
        EnableMenuItem(hClientMenu, MM_OPT_7, MF_GRAYED);
        EnableMenuItem(hClientMenu, MM_OPT_8, MF_GRAYED);

        SendMessage(GetParent(hwnd), WM_MDISETMENU,
                    (DWORD) hMenu,
                    (LONG)   hMenuWindow) ;
        DrawMenuBar(GetParent(GetParent(hwnd))) ;

        // Don't need to read from the edit control anymore
        KillTimer(hwnd, 2);

        // Unmap view, close mapping!
        if (pShrMem1)
        {
           UnmapViewOfFile (pShrMem1);
           pShrMem1 = NULL;
        }

        if (hMem1)
        {
           CloseHandle (hMem1);
           hMem1 = NULL;
        }

        // now find match
        hHead = (HANDLE) GetWindowLong(ghwndMain, 0);
        if (hHead && (pHead = (PNODE)LocalLock(hHead)))
        {
            pTrail = pHead;
            while ((pHead->ChildWnd.hThisWnd != hwnd) && (pHead->hNext))
            {
               hTmp = hHead;
               pTrail = pHead;
               hHead = pHead->hNext;
               LocalUnlock(hTmp);

               pHead = (PNODE) LocalLock(hHead);
            }

            if (pHead->ChildWnd.hThisWnd == hwnd)
            {
                pWndInfo = &pHead->ChildWnd;

                if (pTrail == pHead)
                    SetWindowLong(ghwndMain, 0, (LONG) pHead->hNext);
                else
                    pTrail->hNext = pHead->hNext;

                LocalUnlock(hHead);
                LocalFree(hHead);
            }
            else
            {
                LocalUnlock(hHead);
            }
        }
        return DefMDIChildProc(hwnd, message, wParam, lParam);

    }

    case WM_DESTROY:
        return 0l;

    default:
         return DefMDIChildProc(hwnd, message, wParam, lParam);
    }

}

/***************************************************************************\
* About
*
* About dialog proc.
*
\***************************************************************************/

BOOL CALLBACK APIENTRY About( HWND hDlg,
                              UINT message,
                              DWORD wParam,
                              LONG lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            return TRUE;

        case WM_COMMAND:
            if (wParam == IDOK)
                EndDialog(hDlg, wParam);
            break;
    }
    return FALSE;

    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(hDlg);
}

/***************************************************************************\
* MapFileName
*
* MapFileName dialog proc.
*
\***************************************************************************/

BOOL CALLBACK MapFileName( HWND hDlg,
                           UINT message,
                           DWORD wParam,
                           LONG lParam)
{

    switch (message)
    {
    case WM_INITDIALOG:
        return TRUE;

    case WM_COMMAND:
        switch (wParam)
        {
            case IDBTN_OK:
            {
                if (!GetDlgItemText(hDlg, IDEDIT_MAPNAME, gszMapName, 20))
                {
                   strncpy(gszMapName, "MapName1", 10);        // default name
                }
                EndDialog(hDlg, IDBTN_OK);
                break;
            }
        }
    }
    return FALSE;

    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(hDlg);
}

/***************************************************************************\
* FileType
*
* FileType dialog proc.
*
\***************************************************************************/

BOOL CALLBACK FileType( HWND hDlg,
                        UINT message,
                        DWORD wParam,
                        LONG lParam)
{

    switch (message)
    {
       case WM_INITDIALOG:
           return TRUE;

       case WM_COMMAND:
           switch (wParam)
           {
              case IDBTN_PAGE:
              {
                 EndDialog(hDlg, IDBTN_PAGE);
                 break;
              }

              case IDBTN_MAP:
              {
                  if ((GetDlgItemText(hDlg, IDEDIT_MAPFILE, gszFile, 20)) == 0)
                      EndDialog(hDlg, IDBTN_PAGE);  // default to use PAGE file
                  else
                      EndDialog(hDlg, IDBTN_MAP);

                  break;
              }
           }
    }
    return FALSE;

    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(hDlg);
}

/*************************************************************************
*
* TextWndProc
*
* Text Window proc.
*
\***************************************************************************/

LONG APIENTRY TextWndProc (HWND hwnd, UINT message, DWORD wParam, LONG lParam)
{
    static HFONT hFont = (HFONT) NULL;

    switch (message)
    {
       case WM_CREATE:
       {
          LOGFONT    lf;
          HDC        hDC;
          HFONT      hOldFont;
          TEXTMETRIC tm;
          RECT       rect;
          LONG       lHeight;

          SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);

          hDC = GetDC(hwnd);
          // this is the height for 8 point size font in pixels
          lf.lfHeight = 8 * GetDeviceCaps(hDC, LOGPIXELSY) / 72;

          hFont = CreateFontIndirect(&lf);
          hOldFont = SelectObject(hDC, hFont);
          GetTextMetrics(hDC, &tm);
          GetClientRect(GetParent(hwnd), &rect);

          // base the height of the window on size of text
          lHeight = tm.tmHeight+6*GetSystemMetrics(SM_CYBORDER)+2;
          // saved the height for later reference
          SetWindowLong(hwnd, GWL_USERDATA, lHeight);
          SetWindowPos(hwnd, NULL, 0, rect.bottom-lHeight,
                       rect.right-rect.left, lHeight,
                       SWP_NOZORDER | SWP_NOMOVE);

          ReleaseDC(hwnd, hDC);
          break;
       }

    case WM_DESTROY:
       if (hFont)
          DeleteObject(hFont);
       break;

    case WM_SETTEXT:
       DefWindowProc(hwnd, message, wParam, lParam);
       InvalidateRect(hwnd,NULL,FALSE);
       UpdateWindow(hwnd);
       return 0L;

    case WM_PAINT:
    {
       PAINTSTRUCT ps;
       RECT   rc;
       char   ach[128];
       int    len, nxBorder, nyBorder;
       HFONT  hOldFont = NULL;

       BeginPaint(hwnd, &ps);

       GetClientRect(hwnd,&rc);

       nxBorder = GetSystemMetrics(SM_CXBORDER);
       rc.left  += 9*nxBorder;
       rc.right -= 9*nxBorder;

       nyBorder = GetSystemMetrics(SM_CYBORDER);
       rc.top    += 3*nyBorder;
       rc.bottom -= 3*nyBorder;

       // 3D Text
       len = GetWindowText(hwnd, ach, sizeof(ach));
       SetBkColor(ps.hdc, GetSysColor(COLOR_BTNFACE));

       SetBkMode(ps.hdc, TRANSPARENT);
       SetTextColor(ps.hdc, RGB(64,96,96));
       if (hFont)
           hOldFont = SelectObject(ps.hdc, hFont);
       ExtTextOut(ps.hdc, rc.left+2*nxBorder+2, rc.top+2,
                  ETO_OPAQUE | ETO_CLIPPED, &rc, ach, len, NULL);

       SetTextColor(ps.hdc, RGB(128,128,128));
       if (hFont)
           hOldFont = SelectObject(ps.hdc, hFont);
       ExtTextOut(ps.hdc, rc.left+2*nxBorder+1, rc.top+1, ETO_CLIPPED,
                   &rc, ach, len, NULL);

       SetTextColor(ps.hdc, RGB(255,255,255));
       if (hFont)
           hOldFont = SelectObject(ps.hdc, hFont);
       ExtTextOut(ps.hdc, rc.left+2*nxBorder, rc.top, ETO_CLIPPED,
                   &rc, ach, len, NULL);

       SetBkMode(ps.hdc, OPAQUE);

       if (hOldFont)
           SelectObject(ps.hdc, hOldFont);

       EndPaint(hwnd, &ps);
       return 0L;
       }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

//---------------------------------------------------------------------------
//
// FUNCTION:    GetStringRes (int id INPUT ONLY)
//
// COMMENTS:    Load the resource string with the ID given, and return a
//              pointer to it.  Notice that the buffer is common memory so
//              the string must be used before this call is made a second time.
//
//---------------------------------------------------------------------------

LPTSTR GetStringRes (int id)
{
  static TCHAR buffer[MAX_PATH];

  buffer[0]=0;
  LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
  return buffer;
}


MEMORY.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <string.h>

/*
 * Resource defines
 */
#define WINDOWMENU  1

#ifdef RC_INVOKED
#define ID(id) id
#else
#define ID(id) MAKEINTRESOURCE(id)
#endif

/*
 * Menu ID's
 */
#define MM_ABOUT        8000
#define MM_SERVER       8001
#define MM_CLIENT       8002
#define MM_RESERVE      8003

#define MM_OPT_1        7001
#define MM_OPT_2        7002
#define MM_OPT_3        7003
#define MM_OPT_4        7004
#define MM_OPT_5        7005
#define MM_OPT_6        7006
#define MM_OPT_7        7007
#define MM_OPT_8        7008

#define IDM_CASCADE     30
#define IDM_TILE        31
#define IDM_ARRANGE     32
#define IDM_CLOSEALL    33

#define SIZEOFCAPTIONTEXT       20

/*
 * Dialog ID's
 */
#define IDBTN_PAGE              101
#define IDBTN_MAP               102
#define IDBTN_OK                103

#define IDEDIT_MAPFILE          104
#define IDEDIT_MAPNAME          105

/*
 * String resource ID's
 */

#define  IDS_ERR_INITAPP_FAILED         300
#define  IDS_ERR_CREATE_CHILD_FAILED    301
#define  IDS_ERR_CREATE_DLG_FAILED      302
#define  IDS_ERR_MAPFILE_FAILED         303
#define  IDS_ERR_MAPPING_FAILED         304
#define  IDS_ERR_MAPVIEW_FAILED         305
#define  IDS_ERR_ACCESS_WRITE_FAILED    306
#define  IDS_ERR_ACCESS_READ_FAILED     307
#define  IDS_ERR_OPEN_MAPPING_FAILED    308
#define  IDS_SERVER                     309
#define  IDS_CLIENT                     310
#define  IDS_CREATE_MAP_FILE            311
#define  IDS_CREATE_MAPPING             312
#define  IDS_MAPPING_VIEW               313
#define  IDS_ACCESSING_SERVER_WRITE     314
#define  IDS_OPENING_FILE               315
#define  IDS_ACCESSING_SERVER_READ      316
#define  IDS_SEL_CREATE_FILE_MAPPING    317
#define  IDS_SEL_MAP_VIEW               318
#define  IDS_SEL_ACCESS                 319
#define  IDS_SEL_CREATE_FILE            320
#define  IDS_SEL_ACCESS_READ            321
#define  IDS_SEL_OPEN_FILE              322
#define  IDS_ERROR                      323

extern HWND ghwndClient;
extern HWND ghwndMain;

LPTSTR GetStringRes(int id);
void ErrorOut(LPSTR errstring);
HANDLE CreateMapFile (char *MapFileName);
HANDLE CreateMap (HANDLE FileToBeMapped, LPSTR MapName);
LPVOID MapView (HANDLE hMap);
HANDLE OpenMap(LPSTR MapName);


NMMEMCLI.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include "memory.h"

/************************************************************************
 * void ErrorOut(LPSTR errstring)
 *
 * Purpose: Print out an meainful error code by means of
 *        GetLastError and printf
 *
 * Inputs:  errstring - the action that failed, passed by the
 *                    calling proc.
 *
 * Returns: none
 *
 * Calls:   GetLastError
 *
\************************************************************************/

void ErrorOut(LPSTR errstring)
{
   DWORD Error;
   char  str[80];

   Error= GetLastError();

   wsprintf(str, GetStringRes(IDS_ERROR), errstring, Error);
   MessageBox(ghwndMain, str, NULL, MB_OK);
}

/*************************************************************************
 * HANDLE OpenMap(LPSTR MapName)
 *
 * Purpose: Open the mapping object pointed to by MapName
 *
 * Inputs: none
 *
 * Returns: handle to mapped object or NULL if failure
 *
 * Calls: OpenFileMapping, ErrorOut
 *
\*************************************************************************/

HANDLE OpenMap( LPSTR MapName)
{
   HANDLE hAMap;

   hAMap= OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE,
                          TRUE,
                          MapName);

   if (hAMap == NULL)
   {
      ErrorOut("OpenFileMapping");
      return(NULL);
   }
   else
      return(hAMap);
}


NMMEMSRV.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include "memory.h"

/**************************************************************************
 * HANDLE CreateMapFile(char *MapFileName)
 *
 * Purpose: Create a Map file to map named share memory
 *
 * Inputs:  none
 *
 * Returns: MapFileHandle - a handle to the file
 *                        or NULL if failure
 *
 * Calls:   CreateFile, ErrorOut
 *
\**************************************************************************/
HANDLE CreateMapFile(char *MapFileName)
{
   HANDLE MapFileHandle;

   MapFileHandle= CreateFile(MapFileName,
                             GENERIC_READ | GENERIC_WRITE,
                             FILE_SHARE_READ | FILE_SHARE_WRITE,
                             NULL,
                             CREATE_ALWAYS,
                             FILE_ATTRIBUTE_NORMAL,
                             NULL);

   if (MapFileHandle == INVALID_HANDLE_VALUE)
   {
      ErrorOut("CreateFile");
      return(NULL);
   }
   else
      return(MapFileHandle);

}

/**************************************************************************
 * HANDLE CreateMap(HANDLE FileToBeMapped, char *MapName )
 *
 * Purpose: Create File Mapping object using the open file handle
 *
 * Inputs:  FileToBeMapped - handle to the file
 *
 * Returns: MapHandle - handle to the file mapping object
 *                    or NULL if failure
 *
 * Calls:   CreateFileMapping, ErrorOut
\**************************************************************************/

HANDLE CreateMap(HANDLE FileToBeMapped, LPSTR MapName)
{
   HANDLE MapHandle;

   MapHandle= CreateFileMapping(FileToBeMapped,
                                NULL,
                                PAGE_READWRITE,
                                0,
                                4096,
                                MapName);

   if (MapHandle == NULL)
   {
      ErrorOut("CreateFileMapping");
      return(NULL);
   }
   else
      return(MapHandle);
}

/**************************************************************************
 * LPVOID MapView(HANDLE hMap)
 *
 * Purpose: Map the file mapping object into address space
 *
 * Inputs:  hMap - handle to the mapping object
 *
 * Returns: MappedPointer - pointer to the address space that the
 *                        object is mapped into
 *                        or NULL if failure
 *
 * Calls:   MapViewOfFile, ErrorOut
 *
\**************************************************************************/

LPVOID MapView(HANDLE hMap)
{
   LPVOID MappedPointer;

   MappedPointer= MapViewOfFile(hMap,
                                FILE_MAP_WRITE | FILE_MAP_READ,
                                0, 0, 4096);
   if (MappedPointer == NULL)
   {
      ErrorOut("MapViewOfFile");
      return(NULL);
   }
   else
      return(MappedPointer);
}


MTF_DEFS.H

/*
 *  This is a part of the Microsoft Source Code Samples.

 *
 *This sample code shows the usage of some portions
 *of the NTMS API.
 *
 *Return codes are, for the most part, not checked in
 *this code. See the Programmer's reference for error
 *return information.
 *
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 *
 * Copyright 1997 - 1998 Microsoft Corporation.  All Rights Reserved.
 */

#ifndef _MTF_DEFS_H
#define _MTF_DEFS_H

#include <rpc.h>

// make sure we are byte aligned ...
#pragma pack(1)

// definitions from the MTF spec needed for media label identification ...
//
#define MTF_LABEL_REVISION_LEVEL_MIN 1
#define MTF_LABEL_REVISION_LEVEL_MAX 1

#define MTF_TapeDBLKLabel"TAPE"
#define MTF_TapeDBLKLabelSizestrlen (MTF_TapeDBLKLabel)

// defines for StringType in common block header ...
#define NO_STRINGS0x00
#define ANSI_STR0x01
#define UNICODE_STR0x02

// defines for MEDIA Attributes in tape header descriptior block
#define TAPE_SOFT_FILEMARK_BIT   0x0001
#define TAPE_MEDIA_LABEL_BIT     0x0002

// define MTF_DateTime
typedef struct
{
     BYTETime[5] ;
} MTF_DateTime;

// define MTF_TapeAddress
typedef struct
{
     WORD Size;
     WORD Offset;
} MTF_TapeAddress;

// definition of the MTF_DBLKHdr - this exists in the first part of
// all DBLKs in MTF.
typedef struct
{
     BYTEBlockType[4];
     DWORDBlockAttributes;
     WORDOffsetToFirstStream;
     BYTEOSID;
     BYTEOSVersion;
     DWORDLONGDisplayableSize;
     DWORDLONG          FormatLogicalAddress;
     WORDReservedForMBC;
     WORDSoftwareCompression;
     BYTE             Reserved1[4];
     DWORDControlBlockId;
     BYTEReserved2[4];
     MTF_TapeAddressOSSpecificData;
     BYTEStringType;
     BYTEReserved3;
     WORDHeaderCheckSum;
} MTF_DBLKHdr;

// definition of the MTF_TapeDBLK - this is the first DBLK that is recorded on
// the physical media. This includes the MTF_DBLKHdr
typedef struct
{
     MTF_DBLKHdrBlockHeader;
     DWORD              TapeFamilyID;
     DWORD              TapeAttributes;
     WORDTapeSequenceNumber;
     WORDPasswordEncryptionAlgorithm;
     WORDECCAlgorithm;
     WORDTapeCatalogType;
     MTF_TapeAddress    TapeName;
     MTF_TapeAddress    TapeDescription;
     MTF_TapeAddress    TapePassword;
     MTF_TapeAddress    SoftwareName;
     WORDAlignmentFactor;
     WORDSoftwareVendorId;
     MTF_DateTimeTapeDate;
     BYTE               MTFMajorVersion;
} MTF_TapeDBLK;
//
// The MTF NTMS scratch label defines and struct
//

#define MTF_NTMS_Tag L"MTF Media Label|"
#define MTF_NTMS_TagSize sizeof (MTF_NTMS_Tag)

#define MTF_NTMS_Version L"1.0|"
#define MTF_NTMS_VersionSize sizeof(MTF_NTMS_Version)

#define MTF_NTMS_Vendor L"Microsoft|"
#define MTF_NTMS_VendorSize sizeof(MTF_NTMS_Vendor)

#define MTF_NTMS_ProductId L"NTMS|"
#define MTF_NTMS_ProductIdSize (sizeof(MTF_NTMS_ProductId)-1)

#define MTF_NTMS_TimeSize 40

#define MTF_NTMS_Label L"|"
#define MTF_NTMS_LabelSize (sizeof(MTF_NTMS_Label)-1)

#define MTF_NTMS_Side L"1|"
#define MTF_NTMS_SideSize (sizeof(MTF_NTMS_Side)-1)

#define MTF_NTMS_MediaIDBlank L"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}|"
#define MTF_NTMS_MediaIDSize (sizeof(MTF_NTMS_MediaIDBlank)-1)

#define MTF_NTMS_Domain L"|"
#define MTF_NTMS_DomainSize (sizeof(MTF_NTMS_Domain)-1)

// reserved for future

#define VendorSpecificSize 5

typedef struct
{
BYTETag[MTF_NTMS_TagSize];
BYTEVersion[MTF_NTMS_VersionSize];
BYTEVendor[MTF_NTMS_VendorSize];
BYTEVendorProductId[MTF_NTMS_ProductIdSize];
BYTETime[MTF_NTMS_TimeSize];
BYTELabel[MTF_NTMS_LabelSize]; // not used
BYTESide[MTF_NTMS_SideSize];
BYTEMediaID[MTF_NTMS_MediaIDSize];
BYTEDomainID[MTF_NTMS_DomainSize]; // not used
BYTEVS[VendorSpecificSize];
} NTMS_MTF_MediaLabel;

#endif


NTMSAPI.CPP

/*
 *  This is a part of the Microsoft Source Code Samples.
 *  Copyright 1996 - 1998 Microsoft Corporation.
 *  All rights reserved.
 *
 *This sample code shows the usage of some portions
 *of the NTMS API.
 *
 *Return codes are, for the most part, not checked in
 *this code. See the Programmer's reference for error
 *return information.
 *
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 *
 * Copyright 1997 - 1998 Microsoft Corporation.  All Rights Reserved. *
 */
#include <stdio.h>
#include <windows.h>
#include <iostream.h>
#include "NtmsApi.h"
#include "NtmsApiLabel.h"

#define NO_CONTAINERNULL
#define SIZE_FOR_FIRST_TRY5
#define NULL_PARTID((LPNTMS_NULLGUID)0)
#define ALLOCATE_WAIT0
#defineMOUNT_WAITINFINITE
#defineREQUESTING_ONE_MOUNT1
#define REQUESTING_ONE_DISMOUNTREQUESTING_ONE_MOUNT
#define LABEL_BUFFER_SIZE1024

void writeOmid(HANDLE, BYTE *, DWORD *);
void persistPoolID(NTMS_GUID);
NTMS_GUID recallPoolID();
void persistMediaID(NTMS_GUID);
NTMS_GUID recallMediaID();

// Find the first online library
bool
findLibrary( HANDLE hSession, NTMS_GUID &gLibID, DWORD &dwRetCode )
{
DWORD dwSize, i;
NTMS_GUID *gLibList = NULL;
NTMS_OBJECTINFORMATION oiLibraryInfo;

// Enumerate the libraries
dwSize = SIZE_FOR_FIRST_TRY;
for(;;)
{
if(gLibList != NULL)
free(gLibList);
gLibList = (LPNTMS_GUID)malloc(dwSize*sizeof(NTMS_GUID));

dwRetCode = EnumerateNtmsObject(hSession,
NO_CONTAINER,
gLibList,
&dwSize,
NTMS_LIBRARY,
0);

if(dwRetCode == ERROR_INSUFFICIENT_BUFFER) continue;
if(dwRetCode == ERROR_SUCCESS) break;
free(gLibList);
return(false);
}

// Find the first on-line library
oiLibraryInfo.dwSize = sizeof(oiLibraryInfo);
oiLibraryInfo.dwType = NTMS_LIBRARY;
for(i=0; i < dwSize; ++i)
{
dwRetCode = GetNtmsObjectInformation( hSession,
&(gLibList[i]),
&oiLibraryInfo);
if(oiLibraryInfo.Info.Library.LibraryType == NTMS_LIBRARYTYPE_ONLINE)
{
 gLibID = gLibList[i];
 free(gLibList);
 return(true);
}
}

dwRetCode = ERROR_NOT_FOUND;
free(gLibList);
return (false);
}

// Find the first rewritable media type supported by this library
bool
findMediaType( HANDLE hSession, NTMS_GUID &gMediaID, DWORD &dwRetCode )
{
DWORD dwSize, i;
bool bRetCode;
NTMS_GUID *gTypeList = NULL;
NTMS_GUID gLib;
NTMS_OBJECTINFORMATION oiMediaTypeInfo;

// Get the GUID for the library
if((bRetCode = findLibrary(hSession, gLib, dwRetCode)) == false)
return(false);

// Get the list of media type GUIDS in the library
dwSize = SIZE_FOR_FIRST_TRY;
for(;;)
{
if(gTypeList != NULL)
free(gTypeList);
gTypeList = (LPNTMS_GUID)malloc(dwSize*sizeof(NTMS_GUID));

dwRetCode = EnumerateNtmsObject(hSession,
&gLib,
gTypeList,
&dwSize,
NTMS_MEDIA_TYPE,
0);

if(dwRetCode == ERROR_INSUFFICIENT_BUFFER) continue;
if(dwRetCode == ERROR_SUCCESS) break;
free(gTypeList);
return (false);
}

// Go through the guid list and find a rewritable media type
oiMediaTypeInfo.dwSize = sizeof(oiMediaTypeInfo);
oiMediaTypeInfo.dwType = NTMS_MEDIA_TYPE;
for (i=0; i < dwSize; ++i)
{
dwRetCode = GetNtmsObjectInformation( hSession,
&(gTypeList[i]),
&oiMediaTypeInfo);
if(oiMediaTypeInfo.Info.MediaType.ReadWriteCharacteristics == NTMS_MEDIARW_REWRITABLE)
{
gMediaID = gTypeList[i];
free(gTypeList);
return (true);
}
}

dwRetCode = ERROR_NOT_FOUND;
free(gTypeList);
return (false);

}

// Find the import media pool
bool
findImportPool( HANDLE hSession, NTMS_GUID &gIpID, DWORD &dwRetCode )
{
DWORD dwSize, i;
NTMS_GUID *gPoolList = NULL, gTlPool, gMediaType;
NTMS_OBJECTINFORMATION oiMediaPoolInfo;
bool bRetCode;

// Get the GUID of the media type we're interested in
if((bRetCode = findMediaType(hSession, gMediaType, dwRetCode)) == false)
return(false);

// Get the list of pool GUIDS in the library
dwSize = SIZE_FOR_FIRST_TRY;
for(;;)
{
if(gPoolList != NULL)
free(gPoolList);
gPoolList = (LPNTMS_GUID)malloc(dwSize*sizeof(NTMS_GUID));

dwRetCode = EnumerateNtmsObject(hSession,
NULL,
gPoolList,
&dwSize,
NTMS_MEDIA_POOL,
0);

if(dwRetCode == ERROR_INSUFFICIENT_BUFFER) continue;
if(dwRetCode == ERROR_SUCCESS) break;
free(gPoolList);
return (false);
}

// Go through the guid list and find the top level import pool
oiMediaPoolInfo.dwSize = sizeof(oiMediaPoolInfo);
oiMediaPoolInfo.dwType = NTMS_MEDIA_POOL;
for (i=0; i < dwSize; ++i)
{
dwRetCode = GetNtmsObjectInformation( hSession,
&(gPoolList[i]),
&oiMediaPoolInfo);
if(oiMediaPoolInfo.Info.MediaPool.PoolType == NTMS_POOLTYPE_IMPORT)
{
gTlPool = gPoolList[i];
break;
}
}
if(i == dwSize)
{
dwRetCode = ERROR_NOT_FOUND;
free(gPoolList);
return(false);
}

// Now we need to find the media-specific pool beneath the top level pool,
// so get the list of pool GUIDS in the top level import pool
for(;;)
{
dwRetCode = EnumerateNtmsObject(hSession,
&gTlPool,
gPoolList,
&dwSize,
NTMS_MEDIA_POOL,
0);

if(dwRetCode == ERROR_INSUFFICIENT_BUFFER)
{
free(gPoolList);
gPoolList = (LPNTMS_GUID)malloc(dwSize*sizeof(NTMS_GUID));
continue;
}
if(dwRetCode == ERROR_SUCCESS) break;
free(gPoolList);
return (false);
}

// Go through the guid list and find the proper subpool
oiMediaPoolInfo.dwSize = sizeof(oiMediaPoolInfo);
oiMediaPoolInfo.dwType = NTMS_MEDIA_POOL;
for (i=0; i < dwSize; ++i)
{
dwRetCode = GetNtmsObjectInformation( hSession,
&(gPoolList[i]),
&oiMediaPoolInfo);
if(oiMediaPoolInfo.Info.MediaPool.MediaType == gMediaType)
{
gIpID = gPoolList[i];
free(gPoolList);
return (true);
}
}

dwRetCode = ERROR_NOT_FOUND;
free(gPoolList);
return (false);

}

void
UseCase1()
{
HANDLE hSession;
DWORD dwRetCode;
bool bRetCode;
NTMS_GUID gMediaType;
NTMS_GUID gMediaPool;
NTMS_OBJECTINFORMATION oiInfoBuffer;

// Start an NTMS session
hSession = OpenNtmsSession ("", "ApiSampleApp", 0);
if(hSession == INVALID_HANDLE_VALUE)
dwRetCode = GetLastError();

// Get a GUID for a read/write media type
if((bRetCode = findMediaType(hSession, gMediaType, dwRetCode)) == false)
return;

// Create a pool for this app, returns a GUID
dwRetCode = CreateNtmsMediaPool( hSession,
"API_Sample_Pool",
&gMediaType,
NTMS_OPEN_ALWAYS,
NULL,
&gMediaPool);

// Set this new pool to automatically draw media from the scratch pool
// when allocating by first reading the info associated with the
// pool GUID, changing the "allocate from scratch" bit and then setting
// the pool info
oiInfoBuffer.dwSize = sizeof(oiInfoBuffer);
oiInfoBuffer.dwType = NTMS_MEDIA_POOL;
dwRetCode = GetNtmsObjectInformation( hSession,
&gMediaPool,
&oiInfoBuffer);

oiInfoBuffer.Info.MediaPool.AllocationPolicy =
 NTMS_ALLOCATE_FROMSCRATCH;

dwRetCode = SetNtmsObjectInformation (hSession,
&gMediaPool,
&oiInfoBuffer);

// Remember the pool GUID for future use
persistPoolID(gMediaPool);

// That's all for this scenario
CloseNtmsSession(hSession);
}

void
UseCase2()
{
HANDLE hSession, hDrive;
DWORD dwRetCode, labelBytesWritten = LABEL_BUFFER_SIZE;
NTMS_GUID gPoolID, gMediaID, gDriveID;
NTMS_OBJECTINFORMATION oiDriveInfo;
BYTE labelBuffer[LABEL_BUFFER_SIZE];

// Start an NTMS session
hSession = OpenNtmsSession ("", "DemoApp", 0) ;
if(hSession == INVALID_HANDLE_VALUE)
dwRetCode = GetLastError();

// We need to write some data, but don't have a piece of
// media to write it to.
//
// Get a piece of media to use and remember the guid
gPoolID = recallPoolID();
dwRetCode = AllocateNtmsMedia(hSession,
&gPoolID,
NULL,
&gMediaID,
NTMS_ALLOCATE_NEW,
ALLOCATE_WAIT);
persistMediaID(gMediaID);

// Mount the piece of media in a drive, returns a drive GUID
dwRetCode = MountNtmsMedia(hSession,
&gMediaID,
&gDriveID,
REQUESTING_ONE_MOUNT,
NTMS_MOUNT_ERROR_NOT_AVAILABLE,
NTMS_PRIORITY_NORMAL,
MOUNT_WAIT);

// Open the drive the media is in
oiDriveInfo.dwSize = sizeof(oiDriveInfo);
oiDriveInfo.dwType = NTMS_DRIVE;
dwRetCode = GetNtmsObjectInformation(hSession,
&gDriveID,
&oiDriveInfo);

hDrive = CreateFile(oiDriveInfo.Info.Drive.szDeviceName,
                    GENERIC_READ | GENERIC_WRITE,
                    0,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL);

// Write the OMID and tell NTMS
writeOmid(hDrive, labelBuffer, &labelBytesWritten);
dwRetCode = UpdateNtmsOmidInfo(hSession,
&gMediaID,
NTMS_OMID_TYPE_RAW_LABEL,
labelBytesWritten,
(void *)labelBuffer);

// Write data via Win32 API calls, close device when done.

// write, write, write, ...
CloseHandle(hDrive);

// Dismount the media
dwRetCode = DismountNtmsMedia( hSession,
&gMediaID,
REQUESTING_ONE_DISMOUNT,
0);

// That's all for this scenario
CloseNtmsSession(hSession);
}

void
UseCase3()
{
HANDLE hSession, hDrive;
DWORD dwRetCode, labelBytesWritten = LABEL_BUFFER_SIZE;
NTMS_GUID gMediaID, gDriveID;
NTMS_OBJECTINFORMATION oiDriveInfo;

// Start an NTMS session
hSession = OpenNtmsSession ("", "DemoApp", 0);
if(hSession == INVALID_HANDLE_VALUE)
dwRetCode = GetLastError();

// Mount the piece of media in a drive
gMediaID = recallMediaID();
dwRetCode = MountNtmsMedia(hSession,
&gMediaID,
&gDriveID,
REQUESTING_ONE_MOUNT,
NTMS_MOUNT_ERROR_NOT_AVAILABLE,
NTMS_PRIORITY_NORMAL,
MOUNT_WAIT);

// Open the drive the media is in
oiDriveInfo.dwSize = sizeof(oiDriveInfo);
oiDriveInfo.dwType = NTMS_DRIVE;
dwRetCode = GetNtmsObjectInformation(hSession,
&gDriveID,
&oiDriveInfo);

hDrive = CreateFile(oiDriveInfo.Info.Drive.szDeviceName,
                    GENERIC_READ | GENERIC_WRITE,
                    0,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL);

// Read (and/or write) data via Win32 API calls, Close device when done.

// read, read, read, ...
CloseHandle(hDrive);

// Dismount the media
dwRetCode = DismountNtmsMedia( hSession,
&gMediaID,
REQUESTING_ONE_DISMOUNT,
0);

// We've determined that we no longer need this piece of media
// so deallocate it.
dwRetCode = DeallocateNtmsMedia( hSession,
&gMediaID,
0);

// That's all for this scenario
CloseNtmsSession(hSession);
}

void
UseCase4()
{
HANDLE hSession;
DWORD dwRetCode, i;
NTMS_GUID gIpID, *gPartitionList = NULL, gPoolID;
bool bRetCode;
DWORD dwSize;
NTMS_OBJECTINFORMATION oiPartitionInfo;

// Start an NTMS session
hSession = OpenNtmsSession ("", "DemoApp", 0);
if(hSession == INVALID_HANDLE_VALUE)
dwRetCode = GetLastError();

// The method used here to find media in the import pool is actually the
// harder of two ways to do it.  The easier way  is to enumerate all the
// partitions in NTMS (instead of the media-specific import pool and check
// for ones with the partstate set to PARTSTATE_IMPORT.  This way is more
// efficient and gives you a flavor for traversing nested media pools (which
// is done in findImportPool).

// Get the GUID of the media-specific import pool
if( (bRetCode = findImportPool(hSession, gIpID, dwRetCode)) == false)
return;

// Enumerate all the partitions in the media-specific import pool
dwSize = SIZE_FOR_FIRST_TRY;
for(;;)
{
if(gPartitionList != NULL)
free(gPartitionList);
gPartitionList = (LPNTMS_GUID)malloc(dwSize*sizeof(NTMS_GUID));

dwRetCode = EnumerateNtmsObject(hSession,
&gIpID,
gPartitionList,
&dwSize,
NTMS_PARTITION,
0);

if(dwRetCode == ERROR_INSUFFICIENT_BUFFER) continue;
if(dwRetCode == ERROR_SUCCESS) break;
return;
}

gPoolID = recallPoolID();

// Look at the label on each piece of media & move it
// to this app's pool if it was written by this
// app.
oiPartitionInfo.dwSize = sizeof(oiPartitionInfo);
oiPartitionInfo.dwType = NTMS_PARTITION;
for (i=0; i < dwSize; ++i)
{
dwRetCode = GetNtmsObjectInformation( hSession,
&(gPartitionList[i]),
&oiPartitionInfo);
if((dwRetCode = strncmp(oiPartitionInfo.Info.Partition.szOmidLabelType,
"Microsoft NTMS API Demo", 23)) == 0)
{
MoveToNtmsMediaPool(hSession,
&(oiPartitionInfo.Info.Partition.PhysicalMedia), &gPoolID);
}
}

// That's all for this scenario
CloseNtmsSession(hSession);
}

void
main()
{

UseCase1();// Stuff done during app initialization

UseCase2(); // Typical stuff done during app operations
//including media allocation

UseCase3(); // More typical stuff done during app operations
//including media deallocation

UseCase4();// Find each piece of media in the import pool that
//belong to this app and move it to this
//app's media pool

}


NTMSAPILABEL.H

/*
 *  This is a part of the Microsoft Source Code Samples.
 *
 *This sample code shows the usage of some portions
 *of the NTMS API.
 *
 *Return codes are, for the most part, not checked in
 *this code. See the Programmer's reference for error
 *return information.
 *
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 *
 * Copyright 1997 - 1998 Microsoft Corporation.  All Rights Reserved. *
 */

#include <ntmsmli.h>
#include <tchar.h>

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */

#ifdef MAKING_APIDEMO_LABEL
#define APIDEMO_LABEL_EXPORT __declspec(dllexport)
#else
#define APIDEMO_LABEL_EXPORT __declspec(dllimport)
#endif

// defines for media label identification ...
#define MTF_MediaLabelType_NT_ApiDemoL"Microsoft NTMS API Demo"
#define MTF_MediaLabelType_NT_ApiDemoSizewcslen (MTF_MediaLabelType_NT_ApiDemo)

APIDEMO_LABEL_EXPORT DWORD ClaimMediaLabel(const BYTE * const pBuffer,
     const DWORD nBufferSize,
    MediaLabelInfo * const pLabelInfo) ;

APIDEMO_LABEL_EXPORT DWORD MaxMediaLabel (DWORD * const pMaxSize) ;

#ifdef __cplusplus
}
#endif


NTMSLABL.CPP

/*
 *  This is a part of the Microsoft Source Code Samples.
 *
 *This sample code shows the usage of some portions
 *of the NTMS API.
 *
 *Return codes are, for the most part, not checked in
 *this code. See the Programmer's reference for error
 *return information.
 *
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 *
 * Copyright 1997 - 1998 Microsoft Corporation.  All Rights Reserved.
 */
#include <windows.h>
#include <iostream.h>
#include <stdio.h>
#include <time.h>
#include <rpc.h>
#include <NtmsMli.h>
#include "mtf_defs.h"
#include "ntmsapilabel.h"

WORD XORCheckSum(WORD *, DWORD ) ;

void writeOmid (HANDLE hDevice, BYTE *Buffer, DWORD *BufferSizePtr)
{
// local variables ...
DWORD nError = NO_ERROR ;
MTF_TapeDBLK *plbl ;
BYTE LabelBuffer[1024] ;
time_t CurrentTime;
struct tm *GMTime ;
wchar_t DateString[10] ;
wchar_t TimeString[12] ;
wchar_t SoftwareName[80] ;
wchar_t TapeName[80] ;
WORD nSize ;
WORD nTapeNameLength, nSoftwareNameLength ;
BOOL nRetCode ;
DWORD nBytesWritten ;
BYTE TapeDate[4] ;
void *pTapeName, *pSoftwareName ;

// create and fill in the MTF media label ...
memset (LabelBuffer, 0, 1024) ;
plbl = (MTF_TapeDBLK *) LabelBuffer ;
strncpy ((char *)plbl->BlockHeader.BlockType, MTF_TapeDBLKLabel, MTF_TapeDBLKLabelSize) ;
//plbl->BlockHeader.BlockAttributes = 0x30000 ;
//plbl->BlockHeader.OffsetToFirstStream = 0x400 ;
//plbl->BlockHeader.OSID = 0xe ;
plbl->BlockHeader.StringType = UNICODE_STR ;
plbl->BlockHeader.HeaderCheckSum =
XORCheckSum((WORD *)&plbl->BlockHeader,
sizeof(plbl->BlockHeader) - sizeof(plbl->BlockHeader.HeaderCheckSum)) ;
//plbl->TapeSequenceNumber = 1 ;
//plbl->TapeCatalogType = 1 ;// MTF_OTC_TYPE
//plbl->AlignmentFactor = 1024 ;// 1k alignment factor ...
//plbl->MTFMajorVersion = 1 ;// MTF_FORMAT_VER_MAJOR

// get the current time ...
time(&CurrentTime);

// create the date  and time string ...
GMTime = gmtime(&CurrentTime) ;
wcsftime (DateString, 10, L"%m/%d/%y", GMTime) ;
wcsftime (TimeString, 12, L"%I:%M:%S %p", GMTime) ;

// fill in the Tape Name and the Software Name fields ...
    swprintf(SoftwareName, L"%s (APIDEMO.EXE) Version 1.0 Rev 5",
                 MTF_MediaLabelType_NT_ApiDemo);
swprintf(TapeName, L"Tape created on %s", DateString);

// copy the TapeName and the SoftwareName into the Media Label that we are creating ...
nSize = sizeof (MTF_TapeDBLK) ;
nTapeNameLength = wcslen (TapeName) ;
plbl->TapeName.Size = nTapeNameLength * 2 ;// strings are Unicode (i.e. 2 byte chars)
plbl->TapeName.Offset = nSize ;// size of the MTF_TapeDBLK ...
pTapeName = (void *) ((DWORD) plbl + (DWORD) plbl->TapeName.Offset) ;
wcsncpy ((wchar_t *) pTapeName, TapeName, nTapeNameLength) ;

// setup the SoftwareName ... copy the SoftwareName into the label ...
nSoftwareNameLength = wcslen(SoftwareName) ;plbl->SoftwareName.Size = nSoftwareNameLength * 2 ;   // strings are Unicode (i.e. 2 byte chars)
plbl->SoftwareName.Offset = nSize + plbl->TapeName.Size ;
pSoftwareName = (void *) ((DWORD) plbl + (DWORD) plbl->SoftwareName.Offset) ;
wcsncpy ((wchar_t *) pSoftwareName, SoftwareName, nSoftwareNameLength) ;

// fill in the TapeDate and MediaID fields ...
TapeDate[0] = plbl->TapeDate.Time[1] ;
TapeDate[1] = plbl->TapeDate.Time[2] ;
TapeDate[2] = plbl->TapeDate.Time[3] ;
TapeDate[3] = plbl->TapeDate.Time[4] ;
memcpy ((void *)&(plbl->TapeFamilyID), (void *) TapeDate, 4) ;

// set the tape label bit
plbl->TapeAttributes = TAPE_MEDIA_LABEL_BIT;

// write the data out to the media ...
nRetCode = WriteFile (hDevice, (void *) plbl, 1024,
&nBytesWritten, NULL) ;
if (nRetCode == FALSE)
{
nError = GetLastError() ;
}
    else
    {
        if (BufferSizePtr != NULL)
        {
            if (NULL != Buffer)
            {
                if (*BufferSizePtr >= nBytesWritten)
                {
                    *BufferSizePtr = nBytesWritten;
                    memcpy(Buffer, (void *)plbl, *BufferSizePtr);
                }
            }
            else
            {
                *BufferSizePtr = 0;
            }
        }
    }

    // return
return;
}

// Routine to compute the WORD X-OR CheckSum of the bytes for the number of bytes
//   specified.
WORD XORCheckSum(WORD *pBuffer, DWORD nBytes)
{
    WORD RetVal = 0;
    for (DWORD i = 0; i < nBytes / sizeof(WORD); i++)
    {
        RetVal ^= pBuffer[i];
    }

    return RetVal;
}


NTMSUTLS.CPP

/*
 *  This is a part of the Microsoft Source Code Samples.
 *  Copyright 1996 - 1998 Microsoft Corporation.
 *  All rights reserved.
 *
 *This sample code shows the usage of some portions
 *of the NTMS API.
 *
 *Return codes are, for the most part, not checked in
 *this code. See the Programmer's reference for error
 *return information.
 *
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 *
 * Copyright 1997 - 1998 Microsoft Corporation.  All Rights Reserved. *
 */
#include <stdio.h>
#include <windows.h>
#include <iostream.h>
#include <ntmsapi.h>

static NTMS_GUID globalPoolID, globalMediaID;

// in a real app the following functions use a database or some such
// thing to persist these GUIDs across app invocations.

void
persistPoolID(NTMS_GUID guid)
{
globalPoolID = guid;
}

NTMS_GUID
recallPoolID()
{
return globalPoolID;
}

void
persistMediaID(NTMS_GUID guid)
{
globalMediaID = guid;
}

NTMS_GUID
recallMediaID()
{
return globalMediaID;
}


DLLMAIN.CPP

/*
 *  This is a part of the Microsoft Source Code Samples.
 *  Copyright 1996 - 1998 Microsoft Corporation.
 *  All rights reserved.
 *
 *This sample code shows the usage of some portions
 *of the NTMS API.
 *
 *Return codes are, for the most part, not checked in
 *this code. See the Programmer's reference for error
 *return information.
 *
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 *
 * Copyright 1997 - 1998 Microsoft Corporation.  All Rights Reserved. *
 */
#include <windows.h>

//
//   $Function:: $
//
// $Parameters:: $
//
//    $Content:: $
//
//     $Return:: $
//
BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
{
  return (TRUE);
}


MTF_DEFS.H

/*
 *  This is a part of the Microsoft Source Code Samples.
 *  Copyright 1996 - 1998 Microsoft Corporation.
 *  All rights reserved.
 *
 *This sample code shows the usage of some portions
 *of the NTMS API.
 *
 *Return codes are, for the most part, not checked in
 *this code. See the Programmer's reference for error
 *return information.
 *
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 *
 * Copyright 1997 - 1998 Microsoft Corporation.  All Rights Reserved. *
 */
#ifndef _MTF_DEFS_H
#define _MTF_DEFS_H

#include <rpc.h>

// make sure we are byte aligned ...
#pragma pack(1)

// definitions from the MTF spec needed for media label identification ...
//
#define MTF_TapeDBLKLabel"TAPE"
#define MTF_TapeDBLKLabelSizestrlen (MTF_TapeDBLKLabel)

// defines for StringType in common block header ...
#define NO_STRINGS0x00
#define ANSI_STR0x01
#define UNICODE_STR0x02

// defines for MEDIA Attributes in tape header descriptior block
#define TAPE_SOFT_FILEMARK_BIT   0x0001
#define TAPE_MEDIA_LABEL_BIT     0x0002

// define MTF_DateTime
typedef struct
{
     BYTETime[5] ;
} MTF_DateTime;

// define MTF_TapeAddress
typedef struct
{
     WORD Size;
     WORD Offset;
} MTF_TapeAddress;

// definition of the MTF_DBLKHdr - this exists in the first part of
// all DBLKs in MTF.
typedef struct
{
     BYTEBlockType[4];
     DWORDBlockAttributes;
     WORDOffsetToFirstStream;
     BYTEOSID;
     BYTEOSVersion;
     DWORDLONGDisplayableSize;
     DWORDLONG          FormatLogicalAddress;
     WORDReservedForMBC;
     WORDSoftwareCompression;
     BYTE             Reserved1[4];
     DWORDControlBlockId;
     BYTEReserved2[4];
     MTF_TapeAddressOSSpecificData;
     BYTEStringType;
     BYTEReserved3;
     WORDHeaderCheckSum;
} MTF_DBLKHdr;

// definition of the MTF_TapeDBLK - this is the first DBLK that is recorded on
// the physical media. This includes the MTF_DBLKHdr
typedef struct
{
     MTF_DBLKHdrBlockHeader;
     DWORD              TapeFamilyID;
     DWORD              TapeAttributes;
     WORDTapeSequenceNumber;
     WORDPasswordEncryptionAlgorithm;
     WORDECCAlgorithm;
     WORDTapeCatalogType;
     MTF_TapeAddress    TapeName;
     MTF_TapeAddress    TapeDescription;
     MTF_TapeAddress    TapePassword;
     MTF_TapeAddress    SoftwareName;
     WORDAlignmentFactor;
     WORDSoftwareVendorId;
     MTF_DateTimeTapeDate;
     BYTE               MTFMajorVersion;
} MTF_TapeDBLK;
//
// The MTF NTMS scratch label defines and struct
//

#define MTF_NTMS_Tag L"MTF Media Label|"
#define MTF_NTMS_TagSize sizeof (MTF_NTMS_Tag)

#define MTF_NTMS_Version L"1.0|"
#define MTF_NTMS_VersionSize sizeof(MTF_NTMS_Version)

#define MTF_NTMS_Vendor L"Microsoft|"
#define MTF_NTMS_VendorSize sizeof(MTF_NTMS_Vendor)

#define MTF_NTMS_ProductId L"NTMS|"
#define MTF_NTMS_ProductIdSize (sizeof(MTF_NTMS_ProductId)-1)

#define MTF_NTMS_TimeSize 40

#define MTF_NTMS_Label L"|"
#define MTF_NTMS_LabelSize (sizeof(MTF_NTMS_Label)-1)

#define MTF_NTMS_Side L"1|"
#define MTF_NTMS_SideSize (sizeof(MTF_NTMS_Side)-1)

#define MTF_NTMS_MediaIDBlank L"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}|"
#define MTF_NTMS_MediaIDSize (sizeof(MTF_NTMS_MediaIDBlank)-1)

#define MTF_NTMS_Domain L"|"
#define MTF_NTMS_DomainSize (sizeof(MTF_NTMS_Domain)-1)

// reserved for future

#define VendorSpecificSize 5

typedef struct
{
BYTETag[MTF_NTMS_TagSize];
BYTEVersion[MTF_NTMS_VersionSize];
BYTEVendor[MTF_NTMS_VendorSize];
BYTEVendorProductId[MTF_NTMS_ProductIdSize];
BYTETime[MTF_NTMS_TimeSize];
BYTELabel[MTF_NTMS_LabelSize]; // not used
BYTESide[MTF_NTMS_SideSize];
BYTEMediaID[MTF_NTMS_MediaIDSize];
BYTEDomainID[MTF_NTMS_DomainSize]; // not used
BYTEVS[VendorSpecificSize];
} NTMS_MTF_MediaLabel;

#endif


NTMSMLL.CPP

/*
 *  This is a part of the Microsoft Source Code Samples.
 *  Copyright 1996 - 1998 Microsoft Corporation.
 *  All rights reserved.
 *
 *This sample code shows the usage of some portions
 *of the NTMS API.
 *
 *Return codes are, for the most part, not checked in
 *this code. See the Programmer's reference for error
 *return information.
 *
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 *
 * Copyright 1997 - 1998 Microsoft Corporation.  All Rights Reserved. *
 */
#include <windows.h>
#include "ntmsmll.h"
#include "mtf_defs.h"
//#include <crt\\wchar.h>

//////////////////////////////
//       PROTOTYPES         //
//////////////////////////////
DWORD GetMediaGuid(wchar_t *pDescription, WORD size, GUID &gMediaGuid);

DWORD ClaimMediaLabel(const BYTE * const pBuffer, const DWORD nBufferSize,
      MediaLabelInfo * const pLabelInfo)
{
// local variables
DWORD nError = NO_ERROR ;
MTF_TapeDBLK *pLabel ;
void *pTapeName, *pSoftwareName ;

pLabel = (MTF_TapeDBLK *) pBuffer ;
if (strncmp ((char *) pLabel->BlockHeader.BlockType, MTF_TapeDBLKLabel,
MTF_TapeDBLKLabelSize) == 0)
{
// this is an MTF tape ... make sure the TapeName and SoftwareName
// strings actually have data in them ...
if ((pLabel->TapeName.Size != 0) && (pLabel->SoftwareName.Size != 0))
{
// there are strings in the MTF label ... find the strings that we need ...
pTapeName = (void *) ((DWORD) pLabel + (DWORD) pLabel->TapeName.Offset) ;
pSoftwareName = (void *) ((DWORD) pLabel + (DWORD) pLabel->SoftwareName.Offset) ;

// if this MTF tape uses Unicode strings
if (pLabel->BlockHeader.StringType == UNICODE_STR)
{
// if this is an NTMS API Demo tape ...
if (wcsncmp ((wchar_t *) pSoftwareName, MTF_MediaLabelType_NT_ApiDemo,
MTF_MediaLabelType_NT_ApiDemoSize) == 0)
{
// this is an NTMS API Demo tape ... copy the info into the LabelInfo ...
wcsncpy(pLabelInfo->LabelType, (wchar_t *) pSoftwareName, MTF_MediaLabelType_NT_ApiDemoSize) ;
pLabelInfo->LabelType[MTF_MediaLabelType_NT_ApiDemoSize] = '\0' ;

// Copy the App Description
                    // copying unicode string ... divide the size in half ...
wcsncpy(pLabelInfo->LabelAppDescr, (wchar_t *) pTapeName, (pLabel->TapeName.Size / 2));
pLabelInfo->LabelAppDescr[(pLabel->TapeName.Size / 2)] = '\0' ;

                    // if this tape contains a real description/label
                    if ((TAPE_MEDIA_LABEL_BIT & pLabel->TapeAttributes) &&
                        (0 < pLabel->TapeDescription.Size))
                    {
                        void *pDescription = (void *) ((DWORD)pLabel +
                            (DWORD)pLabel->TapeDescription.Offset);
                        GUID   gMediaGuid;
                        DWORD result = NO_ERROR;
                        result = GetMediaGuid((wchar_t *)pDescription,
                                              pLabel->TapeDescription.Size,
                                              gMediaGuid);
                        if (NO_ERROR == result)
                        {
                            // We found a good GUID in the description, use it
                            pLabelInfo->LabelIDSize = sizeof(gMediaGuid);
                            memcpy(pLabelInfo->LabelID,
                                   &gMediaGuid,
                                   pLabelInfo->LabelIDSize);
                            return NO_ERROR;
                        }
                    }
pLabelInfo->LabelIDSize = sizeof (pLabel->TapeFamilyID) ;
memcpy(pLabelInfo->LabelID, &pLabel->TapeFamilyID, pLabelInfo->LabelIDSize);
return NO_ERROR ;
}
}
else // strings are in ANSI form ...
{
nError = ERROR_BAD_FORMAT ;
}
}
}

// media label was not recognized ... return ERROR_BAD_FORMAT ...
return ERROR_BAD_FORMAT ;
}

DWORD MaxMediaLabel (DWORD * const pMaxSize)
{
// local variables
DWORD nError = NO_ERROR ;

// set the max size for the seagate label ...
*pMaxSize = 1024 ;

return nError ;
}

#define HEX_0 L'0'
#define HEX_9 L'9'
#define HEX_A L'A'
#define HEX_F L'F'
#define OUR_STRING wchar_t*
#define OUR_BAR L'|'
#define OUR_BRACE L'{'

DWORD ConvertHex(OUR_STRING str,int length)
{
DWORD value = 0;
OUR_STRING instr = str;
for(int i=0;i<length;i++){
value = value << 4;
if(instr[i] >= HEX_A && instr[i] <= HEX_F){
value += instr[i]-'A'+10;
} else if(instr[i] >= HEX_0 && instr[i] <= HEX_9){
value += instr[i]-HEX_0;
}
}
return value;
}

//
// TODO - This is the exact same code as in NTBackup.cpp
//        Need to make it a .lib somewhere!!!
//
DWORD GetMediaGuid(OUR_STRING pDescription, WORD size, GUID &gMediaGuid)
{
    //
    // pDescription is a string delimited by "|" characters
    // The GUID we are looking for is the 8th field and has
    // the form "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
    // we need to cram this into a GUID
    //
    //
    int numChars = size / sizeof(wchar_t);
    //
    // Find the 7th "|"
    //
    WORD BarNum=0;
    int CheckChar = 0;
    while ((BarNum < 7) && (CheckChar < numChars))
    {
        if (pDescription[CheckChar++] == OUR_BAR)
        {
            BarNum++;
        }
    }
    if (!(
        (7 == BarNum) &&
        (CheckChar < numChars) &&
        (pDescription[CheckChar] == OUR_BRACE))
       )
    {
        return ERROR_BAD_FORMAT;
    }

OUR_STRING pId = &pDescription[CheckChar];
pId++;
gMediaGuid.Data1 = ConvertHex(pId,8);
pId += 9;
gMediaGuid.Data2 = (WORD)ConvertHex(pId,4);
pId += 5;
gMediaGuid.Data3 = (WORD)ConvertHex(pId,4);
pId += 5;
int i;
for(i=0;i<2;i++){
gMediaGuid.Data4[i] = (BYTE)ConvertHex(pId,2);
pId += 2;
}
pId++;
for(i=2;i<8;i++){
gMediaGuid.Data4[i] = (BYTE)ConvertHex(pId,2);
pId += 2;
}
pId++;

    return NO_ERROR;
}


NTMSMLL.H

/*
 *  This is a part of the Microsoft Source Code Samples.
 *  Copyright 1996 - 1998 Microsoft Corporation.
 *  All rights reserved.
 *
 *This sample code shows the useage of some portions
 *of the NTMS API.
 *
 *Return codes are, for the most part, not checked in
 *this code. See the Programmer's reference for error
 *return information.
 *
 */

#include <ntmsmli.h>
#include <tchar.h>

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */

#ifdef MAKING_APIDEMO_LABEL
#define APIDEMO_LABEL_EXPORT __declspec(dllexport)
#else
#define APIDEMO_LABEL_EXPORT __declspec(dllimport)
#endif

// defines for media label identification ...
#define MTF_MediaLabelType_NT_ApiDemoL"Microsoft NTMS API Demo"
#define MTF_MediaLabelType_NT_ApiDemoSizewcslen (MTF_MediaLabelType_NT_ApiDemo)

APIDEMO_LABEL_EXPORT DWORD ClaimMediaLabel(const BYTE * const pBuffer,
     const DWORD nBufferSize,
    MediaLabelInfo * const pLabelInfo) ;

APIDEMO_LABEL_EXPORT DWORD MaxMediaLabel (DWORD * const pMaxSize) ;

#ifdef __cplusplus
}
#endif


README.TXT

Using the Win32 API to Access the Registry

SUMMARY
=======

The REGISTRY sample is a simple utility which demonstrates the functions
needed to access the registry. The sample goes up and down the branches of
the registry tree, displaying the individual key's data values. The sample
can also print specified trees to a file named Registry.Txt.

MORE INFORMATION
================

Usage
-----

Start the sample. A dialog box appears with several edit fields, list boxes,
and buttons. The list box at the center of the dialog box (labeled CHILD
KEYS at the bottom) always holds the child keys of the current key.
Initially, it has four entries, representing the predefined key handles of
the registry:

    HKEY_LOCAL_MACHINE
    HKEY_CURRENT_USER
    HKEY_USERS
    HKEY_CLASSES_ROOT

If you double-click on any of these entries, or highlight one and press the
Next/Down button; the key that you selected appears in the Key Name edit
field, and the children of that selected key replace the entries in the
CHILD KEYS list box. To proceed deeper into the tree, double-click another
child. To back out of the registry, double-click on the ".." at the top of
the list box, or press the Back/Up button.

If the current key has values associated with it, the name of the values is
listed in the right list box (labeled VALUES). If it has no values, VALUES
is followed by a "0". Once you come to a key that does have values
associated with it, you can double-click on any of the values in this list
box. At the bottom of the dialog box are two edit fields: Value: Data Type
and Value: Data Entry. By double-clicking a value entry, these edit fields
are filled with the data's type and the data's value.

If the current key has a class type associated with it, it appears in the
Class edit field. The ACL edit field is not implemented at this time.


MONKEY.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*************************************************************************\
*
*  PROGRAM: Monkey: the Registry Monkey Utility.
*  PURPOSE: To demonstrate Registry API.
*  COMMENTS:
*
\*************************************************************************/

#define STRICT
#include <windows.h>
#include <string.h>
#define _MBCS
#include <mbstring.h>
#include <stdlib.h>
#include <stdio.h>
#include "monkey.h"

HANDLE hInst;
HWND   hDlg;

HANDLE hHeap;

/*************************************************************************\
*
*  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
*
*  PURPOSE: Creates the dialogbox.
*
*  COMMENTS:
*
\*************************************************************************/

int APIENTRY WinMain (HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPSTR     lpCmdLine,
                      int       nCmdShow)

{
  DWORD retCode;

  UNREFERENCED_PARAMETER( nCmdShow );
  UNREFERENCED_PARAMETER( lpCmdLine );
  UNREFERENCED_PARAMETER( hPrevInstance );

  hInst   = hInstance;
  hHeap   = HeapCreate (0, 0, 0);

  retCode = DialogBox ((HANDLE)hInst, (LPCSTR)"MonkeyDlg",
                        NULL, (DLGPROC)MonkeyDlgProc);

  HeapDestroy (hHeap);
  return  (retCode);

}

/************************************************************************\
*
*  FUNCTION: MonkeyDlgProc();
*
*  PURPOSE:  Handle the Monkey dialog box messages.
*
*  MESSAGES:
*
*    WM_INITDIALOG  - Posts WM_GETFIRSTKEY message.
*
*    WM_GETFIRSTKEY - Puts the first 4 pre-defined keys in the listbox.
*
*    IDL_LISTBOX    - Trapped when an item in the left hand listbox
*                     has been double clicked.  It posts a IDB_NEXT message.
*
*    IDL_LISTBOX2   - Trapped when an item in the right hand listbox has
*                     been double clicked.  It basically calls DisplayKeyData,
*                     which fills the Value edit fields with the data from
*                     the current key's specified value information.
*
*    IDB_PRINT      - Basically calls PrintTree() which does a recursive
*                     print of the Registry from the current key to the
*                     end of it's branches.
*
*    IDB_BACK       - Sets the dialog box with the information from the
*                     previously selected key (one closer to the root of
*                     the registry, the parent of the current key).
*
*    IDB_NEXT       - Sets the dialog box with the information on the
*                     selected key child.
*
*    IDR_FULL       - Sets a global variable used to determine if the
*                     user wants to print full Registry information
*                     or only information from keys that have value
*                     associated with it.  Variable is set to TRUE.
*
*    IDR_TRIMMED    - Same as above, only the variable is set to FALSE.
*
\************************************************************************/

int APIENTRY MonkeyDlgProc (HWND hDlg, WORD wMsg, LONG wParam, LONG lParam)
  {
   ULONG    KeyClassLength = 256;
   ULONG    KeyNameLength = 256;
   DWORD    indexLB;
   CHAR     *putNullAt;
   CHAR     lpBuffer1[128];
   CHAR     lpBuffer2[128];

   static   CHAR     RegPath[MAX_PATH]  = "";
   static   CHAR     NameLBSelect[256]  = "";
   static   HKEY     hKeyRoot;
   static   DWORD    RegLevel;
   static   BOOL     FullBranches = TRUE;

   static   HANDLE   hFile = INVALID_HANDLE_VALUE;
   static   HANDLE   hBootIni;

  UNREFERENCED_PARAMETER( lParam );

  switch (wMsg)
    {
      case WM_INITDIALOG:
          // Post a message to get the first 4 pre-defined keys, and set
          // Full Branches to be the print default.
        PostMessage (hDlg, WM_GETFIRSTKEY, 0, 0);
        CheckDlgButton (hDlg, IDR_FULL, TRUE);
        return (0);

      case WM_GETFIRSTKEY:
          // Initialize by putting the first 4 predefined keys of the
          // registry in the list box.

        SendMessage (GetDlgItem(hDlg, IDL_LISTBOX),
                     LB_ADDSTRING, 0, (LONG)"HKEY_LOCAL_MACHINE");

        SendMessage (GetDlgItem(hDlg, IDL_LISTBOX),
                     LB_ADDSTRING, 0, (LONG)"HKEY_CURRENT_USER");

        SendMessage (GetDlgItem(hDlg, IDL_LISTBOX),
                     LB_ADDSTRING, 0, (LONG)"HKEY_USERS");

        SendMessage (GetDlgItem(hDlg, IDL_LISTBOX),
                     LB_ADDSTRING, 0, (LONG)"HKEY_CLASSES_ROOT");

        hKeyRoot = 0;                   // Initialize hKeyRoot.
        return (0);

      case WM_SYSCOMMAND:
        if (wParam == SC_CLOSE)
          {
          EndDialog (hDlg, TRUE);
          if (hFile != INVALID_HANDLE_VALUE)
            CloseHandle (hFile);
          return (TRUE);
          }
        break;

      case WM_COMMAND:

        switch (LOWORD(wParam))
          {
            case IDR_FULL:
                // If Full Branches pressed, set global var to TRUE.
              FullBranches = TRUE;
              return (0);

            case IDR_TRIMMED:
                // If Trimmed Branches pressed, set global var to FALSE.
              FullBranches = FALSE;
              return (0);

            case IDL_LISTBOX:
                 // If double click in left hand listbox, clear Value
                 // edit fields, and execute Next functionality.
               if ( HIWORD (wParam) == LBN_DBLCLK)
                {
                 SetDlgItemText (hDlg, IDE_VALUE1, "");
                 SetDlgItemText (hDlg, IDE_VALUE2, "");
                 PostMessage (hDlg, WM_COMMAND, IDB_NEXT, 0);
                }
               return (0);

            case IDL_LISTBOX2:
                 // If double click right hand listbox, clear Value edit
                 // fields, then display the key's data.
               if ( HIWORD (wParam) == LBN_DBLCLK)
                {
                 SetDlgItemText (hDlg, IDE_VALUE1, "");
                 SetDlgItemText (hDlg, IDE_VALUE2, "");
                 DisplayKeyData (hDlg, RegPath, hKeyRoot);
                }
               return (0);

            case IDB_NEXT:
                 // Get the index of the cursor selection
                 // in the list box.
               indexLB = SendMessage (GetDlgItem (hDlg, IDL_LISTBOX),
                                      LB_GETCURSEL, 0, 0);

                 // If nothing is selected, flag user and return, otherwise
                 // process the selected key.
                                       // LB_ERR indicates nothing selected.
               if (indexLB == LB_ERR)
                 {
                 LoadString(hInst, IDS_SELECTMSG, lpBuffer1, sizeof(lpBuffer1));
                 LoadString(hInst, IDS_NAME, lpBuffer2, sizeof(lpBuffer2));
                 MessageBox (hDlg, lpBuffer1, lpBuffer2, MB_OK);
                 return (0);
                 }

                 // If listbox item 0 is pressed, user wants to move
                 // back up.  Execute the Back functionality.
               if (indexLB == 0 && hKeyRoot)
                {
                PostMessage (hDlg, WM_COMMAND, IDB_BACK, 0);
                return (0);
                }

                                       // Get text from selection in LB.
               SendMessage (GetDlgItem (hDlg, IDL_LISTBOX),
                            LB_GETTEXT, indexLB, (LPARAM)NameLBSelect);

                                       // Put name of chosen item in Name field.
               SetDlgItemText (hDlg, IDE_NAME, NameLBSelect);

                                       // Then clear ListBox entries.
               SendMessage (GetDlgItem (hDlg, IDL_LISTBOX),
                            LB_RESETCONTENT, 0, 0);
               SendMessage (GetDlgItem (hDlg, IDL_LISTBOX2),
                            LB_RESETCONTENT, 0, 0);

               EnumerateLevel (hDlg, NameLBSelect, RegPath, &hKeyRoot);

               return (0);

            case IDB_BACK:

                 // For this case (hRootKey = 0)you're at the top level already.
                 // Tell the user, then return
               if (!hKeyRoot)
                 {
                 LoadString(hInst, IDS_TOPLEVEL, lpBuffer1, sizeof(lpBuffer1));
                 LoadString(hInst, IDS_NAME, lpBuffer2, sizeof(lpBuffer2));
                 MessageBox (hDlg, lpBuffer1, lpBuffer2, MB_OK);
                 return (0);
                 }

                 //For all remaining cases, clear the listboxes.
               SendMessage (GetDlgItem (hDlg, IDL_LISTBOX),
                            LB_RESETCONTENT, 0, 0);
               SendMessage (GetDlgItem (hDlg, IDL_LISTBOX2),
                            LB_RESETCONTENT, 0, 0);

                 // If hRootKey has a value, but the pathname is blank,
                 // then you must be 1 level deep, reset to level 0 by
                 // posting WM_GETFIRSTKEY.
               if (strcmp (RegPath, "") == 0)
                 {
                 SetDlgItemText (hDlg, IDE_NAME, "");
                 PostMessage (hDlg, WM_GETFIRSTKEY, 0, 0);
                 return (0);
                 }

                 // Two cases left.  One in which the path has only one
                 // key name in it, and no back slash character (meaning
                 // strrchr() will return NULL); and one the other case
                 // where there are more than one key name in the path (
                 // and at least one back slash for strrchr().  If this
                 // is the first case, we want to fakeout EnumerateLevel
                 // into thinking we just picked one of the pre-defined keys,
                 // and then re-enumerate it's child keys.
               if ((putNullAt = _mbsrchr (RegPath, '\\')) == NULL)
                 {
                 RegPath[0] = '\0';

                 switch ((DWORD)hKeyRoot)
                   {
                   case (DWORD)HKEY_LOCAL_MACHINE:
                     strcpy (NameLBSelect, "HKEY_LOCAL_MACHINE");
                     break;

                   case (DWORD)HKEY_USERS:
                     strcpy (NameLBSelect, "HKEY_USERS");
                     break;

                   case (DWORD)HKEY_CURRENT_USER:
                     strcpy (NameLBSelect, "HKEY_CURRENT_USER");
                     break;

                   case (DWORD)HKEY_CLASSES_ROOT:
                     strcpy (NameLBSelect, "HKEY_CLASSES_ROOT");
                     break;
                   }
                 SetDlgItemText (hDlg, IDE_NAME, NameLBSelect);
                 hKeyRoot = 0;
                 EnumerateLevel (hDlg, NameLBSelect, RegPath, &hKeyRoot);
                 }
               else
                 {
                   // In the final case, we can just trim the last key
                   // name off the path, and re-enumerate the level.
                 *putNullAt = '\0';
                 putNullAt = _mbsrchr (RegPath, '\\');

                 if (putNullAt)
                   {
                   strcpy (NameLBSelect, putNullAt+1);
                   *putNullAt = '\0';
                   }
                 else
                   {
                   strcpy (NameLBSelect, RegPath);
                   *RegPath = '\0';
                   }
                 SetDlgItemText (hDlg, IDE_NAME, NameLBSelect);
                 EnumerateLevel (hDlg, NameLBSelect, RegPath, &hKeyRoot);
                 }
               return (0);

            default:
               return (0);

          }

     }
    return (FALSE);

  }

/************************************************************************\
*
*  FUNCTION: EnumerateLevel();
*
*  PURPOSE: To get a valid key handle (either to determine if the one sent
*           to the function was one of the pre-defined, or to open a key
*           specified by the path), and to pass that key handle along
*           to QueryKey().
*
*           To enumerate the children of a key, you must have
*           an open handle to it.  The four top keys of the
*           Registry are predefined and open for use:
*           HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_USER,
*           and HKEY_CLASSES_ROOT.  These 4 can be used for
*           RegEnumKey as is; but to RegEnumKey on any of the
*           children of these you must first have an open key
*           handle to the child.
*
*           If hKeyRoot != 0, assume you are lower than the
*           first level of the Registry and the user is trying
*           to enumerate one of the children.  First calculate
*           the name of the child, and then use RegOpenKey to
*           get an open handle.
*
*           If hKeyRoot == 0, assume you are at the top level
*           of the Registry, and set the hKey to be enumerated
*           to be one of the 4 predefined values, the specific
*           one indicated by the ListBox selection.
*
\************************************************************************/
  VOID EnumerateLevel (HWND hDlg, LPTSTR NameLBSelect,
                       LPTSTR RegPath, HKEY *hKeyRoot)
  {

    HKEY hKey;
    DWORD  retCode;
    CHAR   Buf[100];
    CHAR   lpBuffer[128];

    if (*hKeyRoot)
      {
                                       // If RegPath is not NULL, then
                                       // you have to add a backslash to the
                                       // path name before appending the next
                                       // level child name.
      if (strcmp (RegPath, "") != 0)
        strcat (RegPath, "\\");

                                       // Add the next level child name.
      strcat (RegPath, NameLBSelect);

                                       // Use RegOpenKeyEx() with the new
                                       // Registry path to get an open handle
                                       // to the child key you want to
                                       // enumerate.
      retCode = RegOpenKeyEx (*hKeyRoot,
                              RegPath,
                              0,
                              KEY_ENUMERATE_SUB_KEYS |
                              KEY_EXECUTE |
                              KEY_QUERY_VALUE,
                              &hKey);

      if (retCode != ERROR_SUCCESS)
        {
        if (retCode == ERROR_ACCESS_DENIED) {
          LoadString(hInst, IDS_CANTOPENKEY, lpBuffer, sizeof(lpBuffer));
          wsprintf (Buf, lpBuffer);
        }
        else {
          LoadString(hInst, IDS_OPENKEYERR, lpBuffer, sizeof(lpBuffer));
          wsprintf (Buf, lpBuffer, retCode, __LINE__);
        }
        MessageBox (hDlg, Buf, "", MB_OK);
        PostMessage (hDlg, WM_COMMAND, IDB_BACK, 0);
        return;
        }

      }
    else
      {
                                       // Set the *hKeyRoot handle based
                                       // on the text taken from the ListBox.

      if (strcmp (NameLBSelect, "HKEY_CLASSES_ROOT") == 0)
        *hKeyRoot = HKEY_CLASSES_ROOT;

      if (strcmp (NameLBSelect, "HKEY_USERS") == 0)
        *hKeyRoot = HKEY_USERS;

      if (strcmp (NameLBSelect, "HKEY_LOCAL_MACHINE") == 0)
        *hKeyRoot = HKEY_LOCAL_MACHINE;

      if (strcmp (NameLBSelect, "HKEY_CURRENT_USER") == 0)
        *hKeyRoot = HKEY_CURRENT_USER;

      hKey = *hKeyRoot;     // hKey is used in RegEnumKey().

      }//end if/else *hKeyRoot

      QueryKey (hDlg, hKey);

      RegCloseKey (hKey);   // Close the key handle.

//      rect.top = 0; rect.left = 5; rect.right = 1200; rect.bottom = 25;
//      hDC = GetDC (hDlg);
//      FillRect (hDC, &rect, GetStockObject(WHITE_BRUSH));
//      TextOut (hDC, 5, 5, RegPath, strlen(RegPath));
//      ReleaseDC (hDlg, hDC);
      SetDlgItemText (hDlg, IDE_TEXTOUT, RegPath);

  }

/************************************************************************\
*
*  FUNCTION: QueryKey();
*
*  PURPOSE:  To display the key's children (subkeys) and the names of
*            the Values associated with it.  This function uses RegEnumKey,
*            RegEnumValue, and RegQueryInfoKey.
*
\************************************************************************/
VOID QueryKey (HWND hDlg, HANDLE hKey)
  {
  CHAR     KeyName[MAX_PATH];
  CHAR     ClassName[MAX_PATH] = ""; // Buffer for class name.
  DWORD    dwcClassLen = MAX_PATH;   // Length of class string.
  DWORD    dwcSubKeys;               // Number of sub keys.
  DWORD    dwcMaxSubKey;             // Longest sub key size.
  DWORD    dwcMaxClass;              // Longest class string.
  DWORD    dwcValues;                // Number of values for this key.
  DWORD    dwcMaxValueName;          // Longest Value name.
  DWORD    dwcMaxValueData;          // Longest Value data.
  DWORD    dwcSecDesc;               // Security descriptor.
  FILETIME ftLastWriteTime;          // Last write time.

  DWORD i;
  DWORD retCode;

  DWORD j;
  DWORD retValue;
  CHAR  ValueName[MAX_VALUE_NAME];
  DWORD dwcValueName = MAX_VALUE_NAME;
  CHAR  Buf[80];
  CHAR  lpBuffer[80];

  // Get Class name, Value count.

  RegQueryInfoKey (hKey,              // Key handle.
                   ClassName,         // Buffer for class name.
                   &dwcClassLen,      // Length of class string.
                   NULL,              // Reserved.
                   &dwcSubKeys,       // Number of sub keys.
                   &dwcMaxSubKey,     // Longest sub key size.
                   &dwcMaxClass,      // Longest class string.
                   &dwcValues,        // Number of values for this key.
                   &dwcMaxValueName,  // Longest Value name.
                   &dwcMaxValueData,  // Longest Value data.
                   &dwcSecDesc,       // Security descriptor.
                   &ftLastWriteTime); // Last write time.

  SetDlgItemText (hDlg, IDE_CLASS, ClassName);
  SetDlgItemInt  (hDlg, IDE_CVALUES, dwcValues, FALSE);

  SendMessage (GetDlgItem (hDlg, IDL_LISTBOX),
               LB_ADDSTRING, 0, (LONG)"..");

                                       // Loop until RegEnumKey fails, get
                                       // the name of each child and enter
                                       // it into the box.

  // Enumerate the Child Keys.

  SetCursor (LoadCursor (NULL, IDC_WAIT));
  for (i=0, retCode = ERROR_SUCCESS; retCode == ERROR_SUCCESS; i++)
    {
    retCode = RegEnumKey (hKey, i,
                          KeyName, MAX_PATH);

    if (retCode == (DWORD)ERROR_SUCCESS)
      SendMessage (GetDlgItem(hDlg, IDL_LISTBOX),
                   LB_ADDSTRING, 0, (LONG)KeyName);
    }
  SetCursor (LoadCursor (NULL, IDC_ARROW));

  // Enumerate the Key Values
        SetCursor (LoadCursor (NULL, IDC_WAIT));

        if (dwcValues)
          for (j = 0, retValue = ERROR_SUCCESS; j < dwcValues; j++)
            {
            dwcValueName = MAX_VALUE_NAME;
            ValueName[0] = '\0';
            retValue = RegEnumValue (hKey, j, ValueName,
                                     &dwcValueName,
                                     NULL,
                                     NULL,               //&dwType,
                                     NULL,               //&bData,
                                     NULL);              //&bcData);
            if (retValue != (DWORD)ERROR_SUCCESS &&
                retValue != ERROR_INSUFFICIENT_BUFFER)
              {
              LoadString(hInst, IDS_REGERR, lpBuffer, sizeof(lpBuffer));
              wsprintf (Buf, lpBuffer, __LINE__, j, retValue, dwcValueName);
              LoadString(hInst, IDS_DEBUG, lpBuffer, sizeof(lpBuffer));
              MessageBox (hDlg, Buf, lpBuffer, MB_OK);
              }

            Buf[0] = '\0';
            if (!strlen(ValueName)) {
              LoadString(hInst, IDS_NONAME, lpBuffer, sizeof(lpBuffer));
              strcpy (ValueName, lpBuffer);
            }
            wsprintf (Buf, "%d) %s ", j, ValueName);
            SendMessage (GetDlgItem (hDlg, IDL_LISTBOX2),
                           LB_ADDSTRING, 0, (LONG)Buf);

            }// end for(;;)

        SetCursor (LoadCursor (NULL, IDC_ARROW));

  }

/************************************************************************\
*
*  FUNCTION: DisplayKeyData();
*
*  PURPOSE:  To display the keys values and value types to the Value edit
*            field.  This function is called when the right hand listbox
*            is double clicked.  The functionality is much like that found
*            in the function PrintTree, please see it for more details.
*
\************************************************************************/

VOID DisplayKeyData (HWND hDlg, CHAR *RegPath, HANDLE hKeyRoot)
  {
  HKEY   hKey;
  DWORD  dwLBIndex;
  CHAR   Buf[LINE_LEN];
  CHAR   ValueName[MAX_VALUE_NAME];
  DWORD  cbValueName = MAX_VALUE_NAME;
  DWORD  dwType;
  DWORD  retCode;
  CHAR   lpBuffer[128];
  CHAR   lpBuffer2[128];

  CHAR   ClassName[MAX_PATH];
  DWORD  dwcClassLen = MAX_PATH;
  DWORD  dwcSubKeys;
  DWORD  dwcMaxSubKey;
  DWORD  dwcMaxClass;
  DWORD  dwcValues;
  DWORD  dwcMaxValueName;
  DWORD  dwcMaxValueData;
  DWORD  dwcSecDesc;
  FILETIME  ftLastWriteTime;

  BYTE   *bData;
  DWORD  cbData;

  CHAR   *outBuf;
  DWORD  i;
  DWORD  cStrLen;

  CHAR   *BinaryStrBuf;
  CHAR   ByteBuf[4];

  CHAR   *ptr;

  // OPEN THE KEY.

                                // LBIndex should == value index.
  dwLBIndex = SendMessage (GetDlgItem (hDlg, IDL_LISTBOX2),
                           LB_GETCURSEL, 0, 0);

  retCode = RegOpenKeyEx (hKeyRoot,    // Key handle at root level.
                          RegPath,     // Path name of child key.
                          0,           // Reserved.
                          KEY_EXECUTE, // Requesting read access.
                          &hKey);      // Address of key to be returned.

  if (retCode)
    {
    LoadString(hInst, IDS_REGOPENERR, lpBuffer, sizeof(lpBuffer));
    wsprintf (Buf, lpBuffer, retCode);
    MessageBox (hDlg, Buf, "DisplayKeyData()", MB_OK);
    return;
    }

// ADD A QUERY AND ALLOCATE A BUFFER FOR BDATA.

  retCode =
  RegQueryInfoKey (hKey,              // Key handle.
                   ClassName,         // Buffer for class name.
                   &dwcClassLen,      // Length of class string.
                   NULL,              // Reserved.
                   &dwcSubKeys,       // Number of sub keys.
                   &dwcMaxSubKey,     // Longest sub key size.
                   &dwcMaxClass,      // Longest class string.
                   &dwcValues,        // Number of values for this key.
                   &dwcMaxValueName,  // Longest Value name.
                   &dwcMaxValueData,  // Longest Value data.
                   &dwcSecDesc,       // Security descriptor.
                   &ftLastWriteTime); // Last write time.

   if (retCode)
    {
    LoadString(hInst, IDS_REGQUERYERR, lpBuffer, sizeof(lpBuffer));
    wsprintf (Buf, lpBuffer, retCode, __LINE__);
    MessageBox (hDlg, Buf, "", MB_OK);
    }

   bData = HeapAlloc (hHeap, 0, dwcMaxValueData);
   cbData = dwcMaxValueData;

  // ENUMERATE THE KEY.

  retCode = RegEnumValue (hKey,        // Key handle returned from RegOpenKeyEx.
                          dwLBIndex,   // Value index, taken from listbox.
                          ValueName,   // Name of value.
                          &cbValueName,// Size of value name.
                          NULL,        // Reserved, dword = NULL.
                          &dwType,     // Type of data.
                          bData,       // Data buffer.
                          &cbData);    // Size of data buffer.

  if (retCode != ERROR_SUCCESS)
    {

    if (dwType < REG_FULL_RESOURCE_DESCRIPTOR)
      {
      LoadString(hInst, IDS_REGENUMERR, lpBuffer, sizeof(lpBuffer));
      wsprintf (Buf, lpBuffer, retCode, cbData, __LINE__);
      MessageBox (hDlg, Buf, "", MB_OK);
      }
    }

  switch (dwType)
    {
//    REG_NONE                    ( 0 )   // No value type
//    REG_SZ                      ( 1 )   // Unicode nul terminated string
//    REG_EXPAND_SZ               ( 2 )   // Unicode nul terminated string
                                            // (with environment variable references)
//    REG_BINARY                  ( 3 )   // Free form binary
//    REG_DWORD                   ( 4 )   // 32-bit number
//    REG_DWORD_LITTLE_ENDIAN     ( 4 )   // 32-bit number (same as REG_DWORD)
//    REG_DWORD_BIG_ENDIAN        ( 5 )   // 32-bit number
//    REG_LINK                    ( 6 )   // Symbolic Link (unicode)
//    REG_MULTI_SZ                ( 7 )   // Multiple Unicode strings
//    REG_RESOURCE_LIST           ( 8 )   // Resource list in the resource map
//    REG_FULL_RESOURCE_DESCRIPTOR ( 9 )  // Resource list in the hardware description

    case REG_NONE:
      LoadString(hInst, IDS_REGNONE, lpBuffer, sizeof(lpBuffer));
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer);
      break;

    case REG_SZ:
      LoadString(hInst, IDS_REGSZ, lpBuffer, sizeof(lpBuffer));
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer);

      outBuf = HeapAlloc (hHeap, 0, cbData + 2);
      *outBuf = '\0';

      strcat (outBuf, "\"");
      strcat (outBuf, bData);
      strcat (outBuf, "\"");

      SetDlgItemText (hDlg, IDE_VALUE2, outBuf);
      HeapFree (hHeap, 0, outBuf);
      break;

    case REG_EXPAND_SZ:
      LoadString(hInst, IDS_REGEXPSZ, lpBuffer, sizeof(lpBuffer));
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer);
      outBuf = HeapAlloc (hHeap, 0, cbData + 2);
      *outBuf = '\0';

      strcat (outBuf, "\"");
      strcat (outBuf, bData);
      strcat (outBuf, "\"");

      SetDlgItemText (hDlg, IDE_VALUE2, outBuf);
      HeapFree (hHeap, 0, outBuf);
      break;

    case REG_BINARY:
      LoadString(hInst, IDS_REGBIN, lpBuffer, sizeof(lpBuffer));
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer);
      SetCursor (LoadCursor (NULL, IDC_WAIT));

      BinaryStrBuf = HeapAlloc (hHeap, 0, (3 * cbData) + 1);
      if (BinaryStrBuf)
        {
        *BinaryStrBuf = '\0';
        *ByteBuf = '\0';
        for (i = 0; i < cbData; i++)
          {
          sprintf (ByteBuf, "%02x ", (BYTE)bData[i]);
          strcat (BinaryStrBuf, ByteBuf);
          }
        SetDlgItemText (hDlg, IDE_VALUE2, BinaryStrBuf);
        }
      else
        {
        LoadString(hInst, IDS_MALLOCERR, lpBuffer, sizeof(lpBuffer));
        LoadString(hInst, IDS_DBGTITLE, lpBuffer2, sizeof(lpBuffer2));
        MessageBox (hDlg, lpBuffer, lpBuffer2, MB_OK);
        }
      SetDlgItemText (hDlg, IDL_LISTBOX2, BinaryStrBuf);
      HeapFree (hHeap, 0, BinaryStrBuf);
      SetCursor (LoadCursor (NULL, IDC_ARROW));

      break;

    case REG_DWORD:
      LoadString(hInst, IDS_REGDWORD, lpBuffer, sizeof(lpBuffer));
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer);
      SetDlgItemInt (hDlg, IDE_VALUE2, *(UINT *)bData, FALSE);
      break;

    case REG_DWORD_BIG_ENDIAN:
      LoadString(hInst, IDS_REGBIGEND, lpBuffer, sizeof(lpBuffer));
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer);
      SetDlgItemInt (hDlg, IDE_VALUE2, *(UINT *)bData, TRUE);
      break;

    case REG_LINK:
      LoadString(hInst, IDS_REGLINK, lpBuffer, sizeof(lpBuffer));
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer);
      SetDlgItemText (hDlg, IDE_VALUE2, bData);
      break;

    case REG_MULTI_SZ:
      LoadString(hInst, IDS_REGMULTI, lpBuffer, sizeof(lpBuffer));
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer);
      SetCursor (LoadCursor (NULL, IDC_WAIT));
                                       // Count the NULLs in the buffer to
                                       // find out how many strings there are.

      for (i=0, cStrLen=4; i < cbData; i++)
        if (!bData[i])
          cStrLen+=4;                  // Add room for two quotes and two
                                       // spaced per string.

      outBuf = HeapAlloc (hHeap, 0, cbData + cStrLen);

      ptr = bData;                     // Set ptr to beginning of buffer.
      *outBuf = '\0';                  // Initialize output string.

      strcat (outBuf, "{ ");           // Do first bracket.
      while (*ptr)                     // Loop til you hit 2 NULLs in a row.
        {
         strcat (outBuf, "\"");        // Put quotes around each string.

strcat (outBuf, ptr);
         strcat (outBuf, "\"  ");
         ptr += strlen(ptr)+1;
        }
      strcat (outBuf, "}");            // Add final bracket.
      SetDlgItemText (hDlg, IDE_VALUE2, outBuf);

      SetCursor (LoadCursor (NULL, IDC_ARROW));
      HeapFree (hHeap, 0, outBuf);                 // free output string.
      break;

    case REG_RESOURCE_LIST:            // CM_RESOURCE_LIST is complex.  Print it
                                       // as a free formed binary data for now.

      LoadString(hInst, IDS_REGRESLST, lpBuffer, sizeof(lpBuffer));
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer);

      BinaryStrBuf = HeapAlloc (hHeap, 0, (3 * cbData) + 1);
      if (BinaryStrBuf)
        {
        *BinaryStrBuf = '\0';
        *ByteBuf = '\0';
        for (i = 0; i < cbData; i++)
          {
          sprintf (ByteBuf, "%02x ", (BYTE)bData[i]);
          strcat (BinaryStrBuf, ByteBuf);
          }
        SetDlgItemText (hDlg, IDE_VALUE2, BinaryStrBuf);
        }
      else
        {
        LoadString(hInst, IDS_MALLOCERR, lpBuffer, sizeof(lpBuffer));
        LoadString(hInst, IDS_DBGTITLE, lpBuffer2, sizeof(lpBuffer2));
        MessageBox (hDlg, lpBuffer, lpBuffer2, MB_OK);
        }
      SetDlgItemText (hDlg, IDL_LISTBOX2, BinaryStrBuf);
      HeapFree (hHeap, 0, BinaryStrBuf);

      break;

    case REG_FULL_RESOURCE_DESCRIPTOR:
      LoadString(hInst, IDS_REGFULLDESC, lpBuffer, sizeof(lpBuffer));
      SetDlgItemText (hDlg, IDE_VALUE1, lpBuffer);
      break;

    default:
      LoadString(hInst, IDS_REGUNDEFINE, lpBuffer, sizeof(lpBuffer));
      wsprintf (Buf, lpBuffer, dwType);
      SetDlgItemText (hDlg, IDE_VALUE1, Buf);
      break;

    } // end switch

    HeapFree (hHeap, 0, bData);
  }


MONKEY.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#define IDE_NAME                    101
#define IDE_TITLEINDEX              102
#define IDE_ACL                     104
#define IDE_LASTWRT                 105
#define IDE_VALUE1                  106
#define IDE_VALUE2                  107
#define IDE_VALUE3                  108
#define IDE_CLASS                   109
#define IDB_NEXT                    111
#define IDB_BACK                    112
#define IDL_LISTBOX                 110
#define IDL_LISTBOX2                103
#define IDE_CVALUES                 113
#define IDB_PRINT                   114
#define IDR_FULL                    115
#define IDE_TEXTOUT                 116
#define IDR_TRIMMED                 117

#define IDS_SELECTMSG               201
#define IDS_NAME                    202
#define IDS_TOPLEVEL                203
#define IDS_CANTOPENKEY             204
#define IDS_OPENKEYERR              205
#define IDS_REGERR                  206
#define IDS_DEBUG                   207
#define IDS_NONAME                  208
#define IDS_REGOPENERR              209
#define IDS_REGQUERYERR             210
#define IDS_REGENUMERR              211
#define IDS_REGNONE                 212
#define IDS_REGSZ                   213
#define IDS_REGEXPSZ                214
#define IDS_REGBIN                  215
#define IDS_MALLOCERR               216
#define IDS_DBGTITLE                217
#define IDS_REGDWORD                218
#define IDS_REGBIGEND               219
#define IDS_REGLINK                 220
#define IDS_REGMULTI                221
#define IDS_REGRESLST               222
#define IDS_REGFULLDESC             223
#define IDS_REGUNDEFINE             224

#define MAX_VALUE_NAME              128
#define MAX_DATA_LEN                1024

#define LINE_LEN                    80

#define BRANCH                      1
#define CHILD                       2
#define OTHER                       3

#define BOOT_INI                    "c:\\boot.ini"
#define REGISTRY_TXT                "registry.txt"

// MAX_PATH is defined as 260 in windef.h

#define WM_GETFIRSTKEY              WM_APP

int  APIENTRY MonkeyDlgProc (HWND, WORD, LONG, LONG);

VOID EnumerateLevel (HWND hDlg, LPTSTR NameLBSelect,
                     LPTSTR RegPath, HKEY *hKeyRoot);

VOID QueryKey (HWND, HANDLE);
VOID DisplayKeyData (HWND, LPTSTR, HANDLE);


README.TXT

        Microsoft Win32 Smart Card Development Kit

---------
Contents:
---------

    1.  Introduction
    2.  Updates
    3.  Documentation
    4.  Contact Information
    5.  Development Tools
    6.  Components
    7.  Installation
    8.  C++ Projects
    9.  Java Project
    10. VB Project
    11. Building .IDL Files

---------------
1. Introduction
---------------

Welcome to the Microsoft Win32 Smart Card SDK. This release provides
the necessary files and information to build retail and debug
in-process server dynamic link libraries (DLLs) and smart card aware
applications for Windows NT version 4.0 or greater, Windows 95 or
greater, on x86 platforms.

For the latest information on Microsoft's smart card products, please
visit http://www.microsoft.com/smartcard/

----------
2. Updates
----------

The SCardCOM example has been aligned with the target PCSC V1.0 Part 6
specification.

These changes require that applications built against previous versions
of the Smart Card SDK be re-compiled, and any errors resolved.  All
other changes simply fix bugs, and should not affect the interfaces.

----------------
3. Documentation
----------------

Descriptions of all programming interfaces are in the help files
that come with the Microsoft Platform SDK.

----------------------
4. Contact Information
----------------------

SmartCardSDK@DISCUSS.MICROSOFT.COM

The above email address is an external alias setup for the
Smart Card SDK. There is information on how to subscribe at
http://www.microsoft.com/sitebuilder/resource/mail.asp

--------------------
5. Development Tools
--------------------

The following tools are required to build the smart card components.

   1. Microsoft Visual C++ version 5.0 or higher (see Note below)
   2. Microsoft Win32 Software Development Kit (SDK)
           v4.0 or greater for Windows NT 4.0 or greater, and Windows 95 or
   greater.
   3. Microsoft Visual J++ version 1.0 or later
   4. ATL 2.0 or later.

Most of the utilities, headers, libraries, documentation, and sample
code in the Win32 Smart Card SDK are useful when building DLLs and
smart card aware applications for Windows NT version 4.0 or greater and
Windows 95 or greater.

There is no compiler, linker, lib utility, or make utility provided
with this SDK supplement.

----------------------------
6. Smart Card SDK Components
----------------------------

The following components are included in this version of the Win32
Smart Card SDK.

    \%MSTOOLS%\samples\winbase\scard

- Readme.txt

    \%MSTOOLS%\include

- scarddat.h
        - scarddat.idl
        - scarderr.h
- scardmgr.h
        - scardmgr.idl
- scardsrv.h
        - scardsrv.idl
- sspguid.h
- sspserr.h
- sspsidl.h
- sspsidl.idl
        - winscard.h
        - winsmcrd.h
        - wrpguid.h

    \%MSTOOLS%\lib

        - scarddat.tlb
- scarddlg.lib
        - scardmgr.tlb
- scardsrv.tlb
- winscard.lib

    \%MSTOOLS%\samples\winbase\scard\aggreg

        - dllaggx.c
- dllaggx.h
- preagg.cpp
- preagg.h
- resagg.h
- scardagg.cpp
- scardagg.def
- scardagg.idl
        - scardagg.mak
- scardagg.rc
        - scardni.cpp
        - scardni.h

    \%MSTOOLS%\samples\winbase\scard\aggreg\test

- aggtest.cpp
- aggtest.mak

    \%MSTOOLS%\samples\winbase\scard\app

        - preapp.cpp
- preapp.h
- resapp.h
- scarddg.cpp
- scarddg.h
        - scardtst.cpp
        - scardtst.h
        - scardtst.mak
        - scardtst.rc

     \%MSTOOLS%\samples\winbase\scard\app\res

        - scardtst.ico
        - scardtst.rc2

    \%MSTOOLS%\samples\winbase\scard\html

        - download.htm
        - example.htm

     \%MSTOOLS%\samples\winbase\scard\java

- ByteBuff.java
- SCard.java
- SCardCmd.java
- SCardISO.java
- SCBase.java
- SCConsnt.java
- Test.java

    \%MSTOOLS%\samples\winbase\scard\llcom

        - scdtest.cpp
        - test.mak

    \%MSTOOLS%\samples\winbase\scard\scardcom

        - dlldatax.c
        - dlldatax.h
- resource.h
- scardath.cpp
- scardath.h
        - scardcom.cpp
- scardcom.def
- scardcom.idl
- scardcom.mak
        - scardcom.rc
        - scardcom.rc2
        - scarddef.h
- scardfil.cpp
        - scardfil.h
- scardman.cpp
        - scardman.h
- scardver.cpp
- scardver.h
        - stdafx.cpp
- stdafx.h
        - wrphelp.cpp
        - wrphelp.h

     \%MSTOOLS%\samples\winbase\scard\scardcom\cppwrap

        - scardwrp.cpp
        - scardwrp.h

     \%MSTOOLS%\samples\winbase\scard\setup

        - webssp.cdf
        - webssp.inf

    \%MSTOOLS%\samples\winbase\scard\ssps

        - readssp.txt

    \%MSTOOLS%\samples\winbase\scard\vb

        - scardvb.frm
        - scardvb.vbp

---------------
7. Installation
---------------

Before running SETUP.EXE, the Win32 Platform SDK must be installed on
the target machine including any updates. If you downloaded the Smart
Card SDK from the Microsoft MSDN Web site, then you must also download
the build environment update in order to get all the latest libraries
and header files that are required to build the samples.

To install the Win32 Smart Card SDK on a development machine, run
SETUP.EXE from the root of the distribution media. The setup program
will prompt for the installation path. The suggested path is your
current %MSTOOLS% path: <YourDrive>:\MSSDK\...)

Along with the SDK, the Smart Card Base Components must be installed
on your system. The system level components are distributed separately
from the SDK components. The

Along with the Smart Card Base Components, a smart card reader and
device driver must be installed on your target machine in order for
your application or service provider DLL to communicate with a smart
card (and reader). Please refer to the Smart Card DDK for information
on how to install the DDK components and device drivers.

---------------
8. C++ Projects
---------------

Visual C++ 5.0 or greater is required to build the  C++ projects.

The following steps should be taken to build the projects from within
Developer Studio:

1) Rebuild the low-level COM component .IDL files. (See Building .IDL Files).

2) Set the following include paths in the Tools\Options\Directories menu
property sheet:
"<Your SDK Install path>\include"
"<Your SDK Install path>\samples\winbase\scard\app"
"<Your SDK Install path>\samples\winbase\scard\scardcom"
"<Your SDK Install path>\samples\winbase\scard\scardcom\cppwrap"

3) Open the example's .mak file in Visual C++.

4) Build the project(s).

Notes:

1) To change the default build type (Debug, Release, etc.) edit the
first four lines of the .mak file selecting an alternate build.

2) The projects may be built from the command line after setting the Visual C++
environment variables.

---------------
9. Java Project
---------------

Visual J++ 1.0 or greater is required to build the Java project.

The following steps should be taken to build the project within Developer
Studio:

1) Create a new Java Project Workspace.

2) Add all the java sample files to the project.

3) Compile the project.

--------------
10. VB Project
--------------

Visual Basic 5.0 or greater is required to build the project.

The following steps should be taken to build the project:

1) Open the .vbp.

2) Make sure the low-level smart card COM (scarddat, scardmgr, scardsrv)
components are referenced in the project.

3) Build the project.

-----------------------
11. Building .IDL Files
-----------------------

To rebuild the smart card low-level COM component .IDL files, use the
following command:

midl /I<Your Visual C++ install Path>\Include <Filename.Idl>

In this case, it is assumed the \Include directory contains all SDK
headers not just smart card specific headers.

These files we need to be rebuilt in order to build the samples.


DLLAGGX.C

// wrapper for dlldata.c

#ifdef _MERGE_PROXYSTUB // merge proxy stub DLL

#define REGISTER_PROXY_DLL //DllRegisterServer, etc.

#pragma comment(lib, "rpcndr.lib")
#pragma comment(lib, "rpcns4.lib")
#pragma comment(lib, "rpcrt4.lib")

#define DllMainPrxDllMain
#define DllRegisterServerPrxDllRegisterServer
#define DllUnregisterServer PrxDllUnregisterServer
#define DllGetClassObject   PrxDllGetClassObject
#define DllCanUnloadNow     PrxDllCanUnloadNow

#include "dlldata.c"
#include "ScardAgg_p.c"

#ifdef _NOPROXY //no midl generated dlldata.c

#define STRICT 1
#include <ole2.h>

BOOL WINAPI PrxDllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{return TRUE;}

STDAPI PrxDllCanUnloadNow(void){return S_OK;}

STDAPI PrxDllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{return CLASS_E_CLASSNOTAVAILABLE;}

STDAPI PrxDllRegisterServer(void){return S_OK;}

STDAPI PrxDllUnregisterServer(void){return S_OK;}

#endif //!PROXY_DELEGATION

#endif //_MERGE_PROXYSTUB


DLLAGGX.H

#ifdef _MERGE_PROXYSTUB

extern "C"
{
BOOL WINAPI PrxDllMain(HINSTANCE hInstance, DWORD dwReason,
LPVOID lpReserved);
STDAPI PrxDllCanUnloadNow(void);
STDAPI PrxDllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
STDAPI PrxDllRegisterServer(void);
STDAPI PrxDllUnregisterServer(void);
}

#endif


PREAGG.CPP

// stdafx.cpp : source file that includes just the standard includes
//  stdafx.pch will be the pre-compiled header
//  stdafx.obj will contain the pre-compiled type information

#include "preagg.h"

#include <atlimpl.cpp>


PREAGG.H

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    StdAfx

Abstract:

Include file for standard system include files, or project
specific include files that are used frequently, but are
changed infrequently for this in-process server (DLL) for
the Smartcard Vendor Wrapper Service Provider (SSP).

Author:

Environment:

    Win32, ATL

Revision History:

--*/

#ifndef __STDAFX_H__
#define __STDAFX_H__

#include <afxwin.h>
#include <afxdisp.h>

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>

//////////////////////////////////////////////////////////////////////////
#endif //__STDAFX_H__


RESAGG.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by ScardAgg.rc
//
#define IDS_SCARDNEWINTERFACE_DESC1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SCARDAGG.CPP

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    SCardAgg

Abstract:

This file contains the outline implementation of the DLL exports
for this in-process server (DLL) for example aggregate object.

Author:

Environment:

    Win32, C++ w/Exceptions, ATL, COM/OLE

Revision History:

Notes:

1) This code is NOT specific to any one ICC. It only supports those methods
that map directly to a low-level ICC COM interface/method.

2) You will need the NT SUR Beta 2 SDK or VC 4.2 in order to build this
project.  This is because you will need MIDL 3.00.15 or higher and
new headers and libs.  If you have VC 4.2 installed, then everything
should already be configured correctly.

Proxy/Stub Information -

To merge the proxy/stub code into the object DLL, add the file
dlldatax.c to the project.  Make sure precompiled headers
are turned off for this file, and add _MERGE_PROXYSTUB to the
defines for the project.

Modify the custom build rule for SCardCOM.idl by adding the
following files to the Outputs.  You can select all of the .IDL
files by expanding each project and holding Ctrl while clicking
on each of them.

SCardAgg_p.c
dlldata.c

To build a separate proxy/stub DLL, run:

nmake -f SCardAggps.mak

, in the project directory.
--*/

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//

#include "preagg.h"
#include "resagg.h"
#include "initguid.h"
#include "ScardAgg.h"
#include "SCardNI.h"
#include "dllaggx.h"

///////////////////////////////////////////////////////////////////////////
// Macros
//
#ifdef _DEBUG
#define TRACE_STR(name,sz) \
TRACE(_T("SCardAgg.DLL: CSCardAggApp::%s: %s\n"), name, sz)
#define TRACE_CODE(name,code) \
TRACE(_T("SCardAgg.DLL: CSCardAggApp::%s: error = 0x%x\n"), name, code)
#define TRACE_CATCH(name,code)TRACE_CODE(name,code)
#define TRACE_CATCH_UNKNOWN(name)TRACE_STR(name,_T("An unidentified exception has occurred!"))
#else
#define TRACE_STR(name,sz)((void)0)
#define TRACE_CODE(name,code)((void)0)
#define TRACE_CATCH(name,code)((void)0)
#define TRACE_CATCH_UNKNOWN(name)((void)0)
#endif  // _DEBUG

/////////////////////////////////////////////////////////////////////////////
//
// ActiveX Template Library (ATL)
//

#define IID_DEFINED
#include "ScardAgg_i.c"

#ifdef _MERGE_PROXYSTUB
extern "C" HINSTANCE hProxyDll;
#endif

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_CSCardNewInterface, CSCardNewInterface)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
//
// DLL Entry Point
//

class CScardAggApp : public CWinApp
{
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
};

CScardAggApp theApp;

/*++

CSCardDatApp::InitInstance:

    Override to perform Windows instance initialization, such as
creating your window objects.

Arguments:

    None

Return Value:

    Nonzero if initialization is successful; otherwise 0.

Author:

    Chris Dudley (cdudley) 2/22/1997

--*/

BOOL CScardAggApp::InitInstance()
{
#ifdef _MERGE_PROXYSTUB
// Initialize the DLL's COM interfaces.
hProxyDll = m_hInstance;
// Initialize the MFC part of this object.
#endif
_Module.Init(ObjectMap, m_hInstance);
return CWinApp::InitInstance();
}

/*++

CSCardDatApp::ExitInstance:

    Override to clean up when your application terminates.

Arguments:

    None

Return Value:

    The application s exit code; 0 indicates no errors, and values greater
than 0 indicate an error. This value is used as the return value from
WinMain.

Author:

    Chris Dudley (cdudley) 2/22/1997

--*/
int CScardAggApp::ExitInstance()
{
_Module.Term();
return CWinApp::ExitInstance();
}

/*++

DllCanUnloadNow:

    This routine determines whether the DLL that implements this function
is in use. If not, the caller can safely unload the DLL from memory.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKThe DLL can be unloaded.
S_FALSEThe DLL cannot be unloaded now.

Author:

    Chris Dudley (cdudley) 2/22/1997

Notes:

1. Remarks
A call to DllCanUnloadNow determines whether the DLL from which
it is exported is still in use. A DLL is no longer in use when
it is not managing any existing objects (the reference count on
all of its objects is 0).

2. Notes to Callers
You should not have to call DllCanUnloadNow directly. OLE calls
it only through a call to the CoFreeUnusedLibraries function.
When it returns S_OK, CoFreeUnusedLibraries safely frees the DLL.

3. Notes to Implementers
You need to implement DllCanUnloadNow in, and export it from,
DLLs that are to be dynamically loaded through a call to the
CoGetClassObject function. (You also need to implement and export
the DllGetClassObject function in the same DLL).

If a DLL loaded through a call to CoGetClassObject fails to export
DllCanUnloadNow, the DLL will not be unloaded until the application
calls the CoUninitialize function to release the OLE libraries.

If the DLL links to another DLL, returning S_OK from DllCanUnloadNow
will also cause the second, dependent DLL to be unloaded. To
eliminate the possibility of a crash, the primary DLL should call
the CoLoadLibrary function, specifying the path to the second DLL
as the first parameter, and setting the auto free parameter to TRUE.
This forces the COM library to reload the second DLL and set it up
for a call to CoFreeUnusedLibraries to free it separately when
appropriate.

DllCanUnloadNow should return S_FALSE if there are any existing
references to objects that the DLL manages.

--*/
STDAPI DllCanUnloadNow(void)
{
#ifdef _MERGE_PROXYSTUB
if (PrxDllCanUnloadNow() != S_OK)
return S_FALSE;
#endif
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return (AfxDllCanUnloadNow()==S_OK && _Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/*++

DllGetClassObject:

    This routine returns a class factory to create an object of the
requested type.

Arguments:

    rclsid - CLSID for the class object.

riid - Reference to the identifier of the interface that communicates
with the class object. Usually, this is IID_IClassFactory
(defined in the OLE headers as the interface identifier for
IClassFactory).

ppv - Indirect pointer to the communicating interface.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
E_INVALIDARGAn argument was invalid or unrecognizable.
E_OUTOFMEMORYNo free memory in process address space
to create requested object.
E_UNEXPECTEDOne of the parameters was of an unexpected
type or value.
S_OKThe object was retrieved successfully.
CLASS_E_CLASSNOTAVAILABLEThe DLL does not support the class
(object definition).

Author:

    Chris Dudley (cdudley) 2/22/1997

Notes:

1. Remarks
If a call to the CoGetClassObject function finds the class object
that is to be loaded in a DLL, CoGetClassObject uses the DLL's
exported DllGetClassObject function.

2. Notes to Callers
You should not call DllGetClassObject directly. When an object
is defined in a DLL, CoGetClassObject calls the CoLoadLibrary
function to load the DLL, which, in turn, calls DllGetClassObject.

3. Notes to Implementers
You need to implement DllGetClassObject in (and export it from)
DLLs that support the OLE Component Object Model.

--*/
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
#ifdef _MERGE_PROXYSTUB
if (PrxDllGetClassObject(rclsid, riid, ppv) == S_OK)
return S_OK;
#endif
return _Module.GetClassObject(rclsid, riid, ppv);
}

/*++

DllRegisterServer:

    This routine instructs an in-process server to create its registry
entries for all classes supported in this server module. If this
function fails, the state of the registry for all its classes is
indeterminate.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
E_OUTOFMEMORYNo free memory in process address space to
create requested object.
E_UNEXPECTEDOne of the parameters was of an unexpected
type or value.
S_OKThe registry entries were created successfully.
SELFREG_E_TYPELIBThe server was unable to complete the
registration of all the type libraries used
by its classes.
SELFREG_E_CLASSThe server was unable to complete the
registration of all the object classes.

Author:

    Chris Dudley (cdudley) 2/22/1997

--*/
STDAPI DllRegisterServer(void)
{
#ifdef _MERGE_PROXYSTUB
HRESULT hRes = PrxDllRegisterServer();
if (FAILED(hRes))
return hRes;
#endif
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}

/*++

DllUnregisterServer:

    This routine instructs an in-process server to remove only those
entries created through DllRegisterServer.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
E_OUTOFMEMORYNo free memory in process address space to create
requested object.
E_UNEXPECTEDOne of the parameters was of an unexpected type
or value.
S_OKThe registry entries were created successfully.
S_FALSEUnregistration of this server's known entries
was successful, but other entries still exist
for this server's classes.

Author:

    Chris Dudley (cdudley) 2/22/1997

Notes:

The server must not disturb any entries that it did not create
which currently exist for its object classes. For example, between
registration and unregistration, the user may have specified a
TreatAs relationship between this class and another. In that case,
unregistration can remove all entries except the TreatAs key and
any others that were not explicitly created in DllRegisterServer.
The Win32 registry functions specifically disallow the deletion
of an entire populated tree in the registry. The server can attempt,
as the last step, to remove the CLSID key, but if other entries
still exist, the key will remain.

--*/
STDAPI DllUnregisterServer(void)
{
#ifdef _MERGE_PROXYSTUB
PrxDllUnregisterServer();
#endif
_Module.UnregisterServer();
return S_OK;
}


SCARDAGG.IDL

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    SCardAgg

Abstract:

    IDL source for SCardAgg.DLL. This in-process server (DLL)
    example shows aggregation of the ISCardISO7816 interface using
    the ATL class library.

Author:

Environment:

    Win32

Revision History:

Notes:

    1. This file will be processed by the MIDL tool to produce
        the type library (SCardAgg.tlb) and marshalling code. A
        header file named SCardAgg.h will be generated by the
        MIDL compiler for the ATL CPP code to use.

    2. Rather than create separate IDL files for the C/C++, VB
        and Java languages, the decision has been made to create
        one IDL file that can be used by any language environment
        using standardized OLE Automation types.

    3. The #define'd constants in this MIDL file will NOT be
        registered into the type library! You will need to define
        these constants in your Visual Basic, Delphi, etc. code
        as needed unless the constant is not defined as a const
        variable in this IDL file. The constants will be defined
        for Java users. And, of course, the C/C++ coders can use
        the header file generated by the MIDL compiler as described
        above.

--*/
cpp_quote("//+-------------------------------------------------------------------------")
cpp_quote("//")
cpp_quote("//  Microsoft Smart Cards Support")
cpp_quote("//  Copyright (C) Microsoft Corporation, 1996-1997.")
cpp_quote("//")
cpp_quote("//")
cpp_quote("// File: SCardAgg.h")
cpp_quote("//")
cpp_quote("//--------------------------------------------------------------------------")

cpp_quote("")
cpp_quote("//////////////////////////////////////////////////////////////////")
cpp_quote("//")
cpp_quote("// Imports")
cpp_quote("//")

#ifndef DO_NO_IMPORTS
    import "oaidl.idl";
    import "SspsIdl.idl";
#endif

interface ISCardNewInterface;

cpp_quote("")
cpp_quote("//////////////////////////////////////////////////////////////////")
cpp_quote("//")
cpp_quote("// Types and Constants")
cpp_quote("//")

cpp_quote("#ifndef _NULL_DEFINED")
    cpp_quote("#define _NULL_DEFINED")
    #define NULL    0
cpp_quote("#endif // !_NULL_DEFINED")

cpp_quote("#ifndef _LPBYTE_DEFINED")
    cpp_quote("#define _LPBYTE_DEFINED")
    typedef [public] BYTE *LPBYTE;
cpp_quote("#endif // !_LPBYTE_DEFINED")

cpp_quote("#ifndef _LPCBYTE_DEFINED")
    cpp_quote("#define _LPCBYTE_DEFINED")
    typedef [public] const LPBYTE LPCBYTE;
cpp_quote("#endif // !_LPCBYTE_DEFINED")

cpp_quote("#ifndef _IByteBuffer_DEFINED")
    cpp_quote("#define _IByteBuffer_DEFINED")
    cpp_quote("#define IByteBuffer IStream")
    #define IByteBuffer IStream
cpp_quote("#endif // !_IByteBuffer_DEFINED")

cpp_quote("#ifndef _LPBYTEBUFFER_DEFINED")
    cpp_quote("#define _LPBYTEBUFFER_DEFINED")
    typedef [unique] LPSTREAM LPBYTEBUFFER;
cpp_quote("#endif // !_LPBYTEBUFFER_DEFINED")

cpp_quote("")
cpp_quote("//////////////////////////////////////////////////////////////////")
cpp_quote("//")
cpp_quote("// ISCardNewInterface Interface")
cpp_quote("//")

cpp_quote("#ifndef _LPSCARDNI_DEFINED")
cpp_quote("#define _LPSCARDNI_DEFINED")

    [
        object,
        uuid(9AF66383-8CCC-11D0-AC8C-A80C5BC9547B),
        dual,
        helpstring("ISCardNewInterface Interface"),
        pointer_default(unique)
    ]
    interface ISCardNewInterface : IDispatch
    {
        // Includes -

        import "oaidl.idl";

        // Pointer(s) -

        typedef [unique] ISCardNewInterface* LPSCARDNI;
        typedef [unique] LPSCARDNI LPSCARDNEWINTERFACE;

        // Properties -

        // Methods -
        [id(0),
         helpstring("Some new interface to support specific ICC functionality.")]
        HRESULT NewInterface([in, defaultvalue(0)]  WORD wParameters,
                             [in, defaultvalue(0)]  DWORD dwLength,
                             [in]   LPBYTEBUFFER pParam,
                             [in,out] LPSCARDCMD* ppCmd);
    };

cpp_quote("#endif // !_LPSCARDNI_DEFINED")

[
    uuid(9AF66381-8CCC-11D0-AC8C-A80C5BC9547B),
    version(1.0),
    helpstring("ScardAggregate 1.0 Type Library")
]
library SCARDAGGLib
{
    importlib("stdole32.tlb");

    [
        uuid(9AF66387-8CCC-11D0-AC8C-A80C5BC9547B),
        helpstring("SCardNewInterface Class")
    ]
    coclass CSCardNewInterface
    {
        [default] interface ISCardNewInterface;
    };

};


SCARDNI.CPP

/*++

Copyright (c) 1996-1997  Microsoft Corporation

Module Name:

    SCardNI

Abstract:

This file contains the outline implementation of the CSCardNewInterface
class showing aggregation example.

Author:

Environment:

    Win32, C++ w/Exceptions, ATL, COM/OLE

Revision History:

Notes:

--*/

//////////////////////////////////////////////////////////////////////////
//
// Includes
//

#include "preagg.h"
#include "ScardAgg.h"
#include "SCardNI.h"
#define IID_DEFINED
//#define CLSID_DEFINED
#include <SCardSrv_i.c>

///////////////////////////////////////////////////////////////////////////
// Macros
//
#ifdef _DEBUG
#define TRACE_STR(name,sz) \
TRACE(_T("SCardAgg.DLL: CSCardNewInterface::%s: %s\n"), name, sz)
#define TRACE_CODE(name,code) \
TRACE(_T("SCardAgg.DLL: CSCardNewInterface::%s: error = 0x%x\n"), name, code)
#define TRACE_CATCH(name,code)TRACE_CODE(name,code)
#define TRACE_CATCH_UNKNOWN(name)TRACE_STR(name,_T("An unidentified exception has occurred!"))
#else
#define TRACE_STR(name,sz)((void)0)
#define TRACE_CODE(name,code)((void)0)
#define TRACE_CATCH(name,code)((void)0)
#define TRACE_CATCH_UNKNOWN(name)((void)0)
#endif  // _DEBUG

/*++

CSCardNewInterface::FinalConstruct:

    This routine defines a final constructor that is called after the constructor
for the template class is called.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

    Chris Dudley (cdudley) 2/22/1997

Notes:

This routine uses the NT4.0+ function CoCreateInstance to create
the ISCardISO7816 object. In WIN95+, use CoGetClassObject returning
the IClassFactory for the object, then use IClassFactory->CreateInstance.

To create an aggregate object, the Controlling IUnknown is passed
in during creation of the object to be aggregated. Is this example,
the ATL function GetControllingUnknown is used. This is ATL only and will
work for aggregate and non-aggregate classes.

    Also, changes must be made to the ATL COM map to support the Aggregation.
See scardni.h.

--*/

HRESULT CSCardNewInterface::FinalConstruct()
{
// Locals.
HRESULT hresult = S_OK;
IUnknown* pUnkOuter = GetControllingUnknown();

try {
// Create ISCardISO7816 as aggregate. Only valid IID is IUnknown!!
hresult = CoCreateInstance(CLSID_CSCardISO7816,
pUnkOuter,// Controlling Unknown
CLSCTX_ALL,
IID_IUnknown,
(LPVOID*) &m_pUnkISCardISO7816);
if (FAILED(hresult))
throw (hresult);
}

catch ( HRESULT hr ) {
hresult = hr;
TRACE_CATCH(_T("FinalConstruct"),hresult);
}

catch (...) {
hresult = E_UNEXPECTED;
TRACE_CATCH(_T("FinalConstruct"), hresult);
}

return (hresult);
}

/*++

CSCardNewInterface::FinalRelease:

    This routine defines a final Release.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

    Chris Dudley (cdudley) 2/22/1997

--*/

HRESULT CSCardNewInterface::FinalRelease()
{
// Locals.
HRESULT hresult = S_OK;

try {
// Release interface completely!!
if (m_pUnkISCardISO7816 != NULL)
while (m_pUnkISCardISO7816->Release()>0);
}

catch (...) {
}

return (hresult);
}

/////////////////////////////////////////////////////////////////////////////
//
// ISupportsErrorInfo Methods
//

/*++

CSCardNewInterface::InterfaceSupportsErrorInfo:

    This routine retrieves a IErrorInfo interface for this object.

Arguments:

    riid - Interface ID.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

    Chris Dudley (cdudley) 2/22/1997

--*/

STDMETHODIMP CSCardNewInterface::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&IID_ISCardNewInterface,
};

for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
//
// ISCardNewInterface Methods
//

/*++

CSCardNewInterface::NewInterface:

This method implements some new interface.

Arguments:

wParameters - P1 and P2 encapsulated

dwLength - Length of reply

pParam - IByteBuffer (a.k.a. IStream)

ppCmd - pointer->pointer->ISCardCmd interface

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

    Chris Dudley (cdudley) 2/22/1997

Notes:

--*/
HRESULT CSCardNewInterface::NewInterface(WORD wParameters,// =0
DWORD dwLength,// =0
LPBYTEBUFFER pParam,
LPSCARDCMD *ppCmd)
{
// Locals
HRESULThresult = S_OK;

try {
// Check parameters...
if ( (pParam == NULL) || (ppCmd == NULL ) )
throw ( (HRESULT) E_FAIL );

// ToDo: Build a new command and return
// Example:
//hresult = (*ppCmd)->BuildCmd(m_byClassId,
//(BYTE) INS_SOME_NEW_INSTRUCTION),
//(BYTE) HIBYTE(wParameters)),// P1
//(BYTE) LOBYTE(wParameters)),// P2
//pParam,// P3+data
//&dwLength);// Le
//if (FAILED(hresult))
//throw (hresult);
}

catch (HRESULT hr) {
hresult = hr;
TRACE_CATCH(_T("NewInterface"),hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("NewInterface"));
}

return hresult;
}


SCARDNI.H

/*++

Copyright (c) 1996-1997  Microsoft Corporation

Module Name:

    SCardNI

Abstract:

This file contains the CSCardNewInterface class showing aggregation
example.

Author:

Environment:

    Win32, C++ w/Exceptions, ATL, COM/OLE

Revision History:

Notes:

--*/

#ifndef __SCARDNI_H__
#define __SCARDNI_H__

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//
#include "resagg.h"       // main symbols
#include <scardsrv.h>

/////////////////////////////////////////////////////////////////////////////
//
// Class Declaration
//
class CSCardNewInterface :
public CComDualImpl<ISCardNewInterface, &IID_ISCardNewInterface, &LIBID_SCARDAGGLib>,
public ISupportErrorInfo,
public CComObjectRoot,
public CComCoClass<CSCardNewInterface,&CLSID_CSCardNewInterface>
{
// Members:
private:
BYTEm_byClassId;
// Pointer to Aggregated interface
CComPtr<IUnknown> m_pUnkISCardISO7816;

public:
// Construction:
CSCardNewInterface() {}

~CSCardNewInterface() {}

HRESULT FinalConstruct();

HRESULT FinalRelease();

// COM inteface(s) map

BEGIN_COM_MAP(CSCardNewInterface)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(ISCardNewInterface)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
// Add the interface for Aggregation below
COM_INTERFACE_ENTRY_AGGREGATE(IID_ISCardISO7816,
m_pUnkISCardISO7816.p)
END_COM_MAP()
//
//DECLARE_NOT_AGGREGATABLE(CSCardFileAccess)
// Note:
// Remove the comment from the line above if you don't want your object to
// support aggregation.  The default is to support it
//

//Registry:

//Simple ALL 1.0 based registry entry
DECLARE_REGISTRY(CSCardNewInterface,
_T("ScardAgg.SCardNewInterface.1"),
_T("ScardAgg.SCardNewInterface"),
IDS_SCARDNEWINTERFACE_DESC,
THREADFLAGS_BOTH)
// Note: Enters the standard class registration into the system
// registry.
//
// Script based...
//DECLARE_REGISTRY_RESOURCEID(IDR_SCardTypeConv)
//

// Allows support for GetControllingUnknown() function call.
DECLARE_GET_CONTROLLING_UNKNOWN()

// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

public:
// ISCardNewInterface
STDMETHOD(NewInterface)(WORD wParameters,// =0
DWORD dwLength,// =0
LPBYTEBUFFER pParam,
LPSCARDCMD *ppCmd);
};

///////////////////////////////////////////////////////////////////////

#endif //__SCARDNI_H__


AGGTEST.CPP

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//

#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <crtdbg.h>
#include <initguid.h>

#include "..\scardagg.h"
#include "scardsrv.h"
#include "scarddat.h"
#include "scardmgr.h"
#define IID_DEFINED
#include "..\scardagg_i.c"
#include "scardsrv_i.c"
#include "scarddat_i.c"
#include "scardmgr_i.c"

/////////////////////////////////////////////////////////////////////////////
//
// Globals
//
LPSCARD g_pISCard = NULL;
LPSCARDDATABASE g_pISCardDatabase = NULL;
LPSCARDLOCATE g_pISCardLocate = NULL;
LPSCARDNEWINTERFACE g_pISCardNewInterface = NULL;
LPSCARDISO7816 g_pISCardISO7816 = NULL;
LPSCARDTYPECONV g_pISCardTypeConv = NULL;
LPSCARDCMD g_pISCardCmd = NULL;

/////////////////////////////////////////////////////////////////////////////
//
// Macros
//

/////////////////////////////////////////////////////////////////////////////
//
// Macros
//

#ifndef SafeCast
#define SafeCast(cast,pt)((cast)(pt))
#endif

#ifndef ASSERT
#define ASSERT_ASSERTE
#endif

#ifndef IN
#define IN
#endif

/////////////////////////////////////////////////////////////////////////////
//
// Functions
//

void _cdecl
ConTrace(
IN LPCTSTR lpszFormat, ...)
{
//
// Helper to do print traces...
//

va_list args;
va_start(args, lpszFormat);

int nBuf;
TCHAR szBuffer[512];
ZeroMemory(szBuffer, SafeCast(DWORD,(512*sizeof(TCHAR))));

nBuf = _vstprintf(szBuffer, lpszFormat, args);
ASSERT(nBuf < sizeof(szBuffer));

_tprintf(szBuffer);
    OutputDebugString(szBuffer);
va_end(args);
}

void _cdecl
CallServer(
void)
{
HRESULThresult = S_OK;

ConTrace(_T("\nSTARTING Object Creation\n=========================\n"));
ConTrace(_T("Calling CoCreateInstance()...SCardDat\n"));

hresult = CoCreateInstance(CLSID_CSCard,
NULL,
CLSCTX_ALL,
IID_ISCard,
(LPVOID*) &g_pISCard);
if (FAILED(hresult))
{
ConTrace(_T("Failed to create CSCard object\n"));
return;
}
ConTrace(_T("Object created\n"));

hresult = CoCreateInstance(CLSID_CSCardTypeConv,
NULL,
CLSCTX_ALL,
IID_ISCardTypeConv,
(LPVOID*) &g_pISCardTypeConv);
if (FAILED(hresult))
{
ConTrace(_T("Failed to create CSCardTypeConv object\n"));
return;
}
ConTrace(_T("Object created\n"));

//***************************************************************
// Create the New interface and the aggregated interface
hresult = CoCreateInstance(CLSID_CSCardNewInterface,
NULL,
CLSCTX_ALL,
IID_ISCardNewInterface,
(LPVOID*) &g_pISCardNewInterface);
if (FAILED(hresult))
{
ConTrace(_T("Failed to create CSCardNewInterface object\n"));
return;
}
ConTrace(_T("Object created\n"));

hresult = CoCreateInstance(CLSID_CSCardNewInterface,
NULL,
CLSCTX_ALL,
IID_ISCardISO7816,
(LPVOID*) &g_pISCardISO7816);
if (FAILED(hresult))
{
ConTrace(_T("Failed to create CSCardISO7816 Aggregated object\n"));
return;
}
ConTrace(_T("Object created\n"));
//
//******************************************************************

hresult = CoCreateInstance(CLSID_CSCardCmd,
NULL,
CLSCTX_ALL,
IID_ISCardCmd,
(LPVOID*) &g_pISCardCmd);
if (FAILED(hresult))
{
ConTrace(_T("Failed to create CSCardCmd object\n"));
return;
}
ConTrace(_T("Object created\n"));

ConTrace(_T("\nDONE!!! with object Creation\n=========================\n"));
}

void _cdecl
CallAggreg()
{
// Locals
HRESULThresult = S_OK;
LPBYTEBUFFERpBuffer1 = NULL,
pBuffer2 = NULL;

// Create a couple of byte buffers for testing
hresult = g_pISCardTypeConv->CreateByteBuffer((ULONG) 1,
&pBuffer1);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create IByteBuffer\n"));
return;
};// if
hresult = g_pISCardTypeConv->CreateByteBuffer((ULONG) 1,
&pBuffer2);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create IByteBuffer\n"));
return;
};// if

// Call one of the aggregated methods
ConTrace(_T("Build/Execute: AppendRecord\n"));
hresult = g_pISCardISO7816->AppendRecord(0,
pBuffer1,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on AppendRecord\n"));
return;
}

// Call the new interface method
ConTrace(_T("Build/Execute: NewInterface\n"));
hresult = g_pISCardNewInterface->NewInterface(0,
0,
pBuffer1,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on NewInterface\n"));
return;
}

}

int
main(
IN int argc,
IN char *argv[ ])
{
int nRet = -1;

try
{
if (FAILED(CoInitialize(NULL)))
throw;

CallServer();
CallAggreg();

#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif

CoUninitialize();

nRet = 0;
}

catch (...)
{
ConTrace(_T("Unhandled exception!!!"));
}

return (nRet);
}

/////////////////////////////////////////////////////////////////////////////


PREAPP.CPP

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    stdafx

Abstract:

Include file for standard system include files, or project
specific include files that are used frequently, but are
changed infrequently for this in-process server (DLL) for
the Smartcard Vendor Wrapper Service Provider (SSP).

Author:

Environment:

    Win32, C++ w/Exceptions, COM/OLE

Revision History:

Notes:

--*/

#include "preapp.h"


PREAPP.H

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    stdafx

Abstract:

Include file for standard system include files, or project
specific include files that are used frequently, but are
changed infrequently for this in-process server (DLL) for
the Smartcard Vendor Wrapper Service Provider (SSP).

Author:

Environment:

    Win32, C++ w/Exceptions, COM/OLE

Revision History:

Notes:

--*/

#define VC_EXTRALEAN// Exclude rarely-used stuff from Windows headers

#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions
#include <afxdisp.h>        // MFC OLE automation classes
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>// MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT

#include <afxpriv.h>// MFC Conversion macros, etc.
#include <initguid.h>// include to avoid DEFINE_MACRO problems


RESAPP.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by SCardTst.rc
//
#define IDM_ABOUTBOX                    0x0010
#define IDD_ABOUTBOX                    100
#define IDS_ABOUTBOX                    101
#define IDD_SCARDTST_DIALOG             102
#define IDS_CLSID_ERROR                 102
#define IDR_MAINFRAME                   128
#define IDC_COM_STATUS                  1000
#define IDC_SCARD_STATUS                1001
#define IDC_REQUEST_LIST                1003
#define IDC_PERFORM_REQUEST             1004
#define IDC_FUNCTION_CALL               1005

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        130
#define _APS_NEXT_COMMAND_VALUE         32771
#define _APS_NEXT_CONTROL_VALUE         1006
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SCARDDG.CPP

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    CSCardTestDlg
CAboutDlg

Abstract:

CSCardTestDlg object implementation file. It creates a com vendor g_Wrapper
object to interact with a smart card. Also contains the CAboutDlg
(about box) class and implementation.

Author:

Environment:

    Win32, C++ w/Exceptions, COM/OLE

Revision History:

Notes:

--*/

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//
#include "preapp.h"
#include "SCardTst.h"
#include "SCardDg.h"
#include "SCardWrp.h"

// Declare Simple global g_Wrapper object
CSCardWrapperg_Wrapper;

// Used for parameters
WCHAR g_wszReaderName[] = L"Bull CP8 Transac 0";
WCHAR g_wszFilename[] = L"3F00";

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
//
// CAboutDlg dialog used for application about box
//

class CAboutDlg : public CDialog
{
public:
CAboutDlg();

//////////////////////////////////////////////////////////////////////////
//
// Dialog Data exchange member varialbe declaration
//
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA

//////////////////////////////////////////////////////////////////////////
//
// Virtual method overrides
//
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

protected:
// CAboutDlg Implementation

/////////////////////////////////////////////////////////////////////////
//
// Dialog message map
//
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

/*++

CAboutDlg::CAboutDlg()

Constructor for the about dialog

Arguments:

none

Return Value:

Author:

--*/

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
////////////////////////////////////////////////////////////////////////
//
// Dialog Data Exchange member variable initialization
//
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}

/*++

CAboutDlg::DoDataExchange()

Method allows the exchange and validation of dialog data

Arguments:

pDX - pointer to CDataExchange object

Return Value:

Author:

--*/

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}

//////////////////////////////////////////////////////////////////////////////
//
// Dialog message map
//

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
//
// CSCardTestDlg dialog
//

/*++

CCardTstDlg::CCardTstDlg()

Constructor for the card test dialog gui

Arguments:

none

Return Value:

Author:

--*/

CSCardTestDlg::CSCardTestDlg(CWnd* pParent /*=NULL*/)
: CDialog(CSCardTestDlg::IDD, pParent)
{

////////////////////////////////////////////////////////////////////////
//
// Dialog Data Exchange member variable initialization
//
//{{AFX_DATA_INIT(CSCardTestDlg)
//}}AFX_DATA_INIT

// load the application icon
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

/*++

CCardTstDlg::DoDataExchange()

Method allows the exchange and validation of dialog data

Arguments:

pDX - pointer to CDataExchange object

Return Value:

Author:

--*/

void CSCardTestDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSCardTestDlg)
DDX_Control(pDX, IDC_SCARD_STATUS, m_editICCStatus);
DDX_Control(pDX, IDC_COM_STATUS, m_editComStatus);
DDX_Control(pDX, IDC_REQUEST_LIST, m_cbRequestList);
//}}AFX_DATA_MAP
}

//////////////////////////////////////////////////////////////////////////////
//
// Dialog message map
//

BEGIN_MESSAGE_MAP(CSCardTestDlg, CDialog)
//{{AFX_MSG_MAP(CSCardTestDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_PERFORM_REQUEST, OnPerformRequestButtonClick)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
//
// CSCardTestDlg message handlers
//

/*++

CCardTstDlg::OnInitDialog()

Allows initialization of smart card test dialog

Arguments:

none

Return Value:

Specifies whether the application has set the input focus to one of the
controls in the dialog box. If OnInitDialog returns nonzero, Windows sets
the input focus to the first control in the dialog box. The application
can return 0 only if it has explicitly set the input focus to one of the
controls in the dialog box.

Author:

--*/

BOOL CSCardTestDlg::OnInitDialog()
{
// locals
BOOLfReturn = TRUE;

CDialog::OnInitDialog();

/////////////////////////////////////////////////////////////////////////
//
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
//
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);

// Retrieve pointer to system menu
CMenu* pSysMenu = GetSystemMenu(FALSE);
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
// Append the About menu item
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}

/////////////////////////////////////////////////////////////////////////
//
//  Set the icon for this dialog.  The framework does this automatically
//  when the application's main window is not a dialog
//
SetIcon(m_hIcon, TRUE);// Set big icon
SetIcon(m_hIcon, FALSE);// Set small icon

///////////////////////////////////////////////////////////////////////////
// Add extra initialization Below

return fReturn;  // return TRUE  unless you set the focus to a control
}

/*++

CCardTstDlg::OnSysCommand()

Contains system menu functionality and max and min button functionality

Arguments:

pID - specifies the type of system command requested
lParam - contains cursor coordinates if the control-menu command
is chosen with the mouse. (low order word contains the x coordinate and
high order word contains the y coordinate)

Return Value:

Author:

--*/

void CSCardTestDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
////////////////////////////////////////////////////////////////////////
//
// open the about dialog box if requested, else pass data through to MFC
//
if ((nID & 0xFFF0) == IDM_ABOUTBOX){
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else{
CDialog::OnSysCommand(nID, lParam);
}
}

/*++

CCardTstDlg::OnPaint()

Allows general window/dialog painting. Allows draws icon when
dialog is minimized or maximized.

Arguments:

none

Return Value:

Author:

--*/

void CSCardTestDlg::OnPaint()
{
// locals

///////////////////////////////////////////////////////////////////////////
// Test status of dialog...If iconic paint the icon, else pass through paint
// for MFC to handle
//
if (IsIconic())
{
CPaintDC dc(this); // device context for painting

SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}

/*++

CCardTstDlg::OnQueryDragIcon()

System calls this to obtain the type of cursor to display while the
iconized dialog is dragged

Arguments:

none

Return Value:

A doubleword value that contains a cursor or icon handle in the low-order
word. The cursor or icon must be compatible with the display
driver s resolution. If the application returns NULL, the system displays
the default cursor. The default return value is NULL

Author:

--*/

HCURSOR CSCardTestDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}

/*++

CCardTstDlg::OnPerformRequestButtonClick()

Processes the selected request in the drop down. This could be a request to
load the COM vendor g_Wrapper, communicate with the smart card, etc.

Arguments:

none

Return Value:

Author:

--*/

void CSCardTestDlg::OnPerformRequestButtonClick()
{
// locals
intiCurrentSelection = m_cbRequestList.GetCurSel();
HRESULThresult = S_OK;
TCHARszStringBuffer[255];
SCARD_STATESStatus;
SCARD_PROTOCOLSProtocol;
BYTEbyChal[8];
HSCARD_FILEhFile;

//BYTEbyData[255];

try {
// What action should be performed
switch (iCurrentSelection) {
// run through the cases
case 0:
// Load COM
hresult = g_Wrapper.LoadCOM();
if (FAILED(hresult))
throw hresult;
else
m_editComStatus.SetWindowText("COM Loaded...");
break;
case 1:
// Attach to reader
hresult = g_Wrapper.AttachToReaderByName(g_wszReaderName);
if (FAILED(hresult))
throw (hresult);
else
m_editComStatus.SetWindowText("Attach to reader was successful.");
break;
case 2:
//Return ICC/IFD status
hresult = g_Wrapper.Status(&Status, &Protocol);
if (FAILED(hresult))
throw(hresult);
m_editComStatus.SetWindowText("Get Status...See Below.");
sprintf(szStringBuffer, "State: %d      Protocol: %d",
(int) Status, (int) Protocol);
m_editICCStatus.SetWindowText(szStringBuffer);
break;
case 3:
//Get challenge..
hresult = g_Wrapper.GetChallenge( (LPBYTE) byChal, 8);
if (FAILED(hresult))
throw (hresult);
m_editComStatus.SetWindowText("Get challenge ok...See Below.");
sprintf(szStringBuffer, "%d %d %d %d %d %d %d %d",
(int) byChal[0], (int) byChal[1], (int) byChal[2], (int) byChal[3],
(int) byChal[4], (int) byChal[5], (int) byChal[6], (int) byChal[7]);
m_editICCStatus.SetWindowText(szStringBuffer);
break;
case 4:
// Change Dir to Master
hresult = g_Wrapper.ChangeDirectory( g_wszFilename );
if (FAILED(hresult))
throw (hresult);
else {
m_editComStatus.SetWindowText("3F00 now selected.");
m_editICCStatus.SetWindowText(" ");
};//else
break;
case 5:
// Open read file
hresult = g_Wrapper.Open( g_wszFilename, &hFile );
if (FAILED(hresult))
throw (hresult);
break;
case 6:
// Verify PIN
if (FAILED(hresult))
throw;
else
m_editComStatus.SetWindowText("PIN Verified.");
break;
case 7:
// Detach
hresult = g_Wrapper.Detach();
if (FAILED(hresult))
throw (hresult);
else {
m_editComStatus.SetWindowText("Detached.");
m_editICCStatus.SetWindowText(" ");
}; //else
default:
break;
};//end switch
}

catch (HRESULT) {
// todo: add error handling
m_editComStatus.SetWindowText("Command Failed...");
m_editICCStatus.SetWindowText(" ");
}

catch (...) {
// todo: add error handling
}
}

/*++

CCardTstDlg::DestroyWindow()

Dialog destroy window override to clean up on dialog close

Arguments:

none

Return Value:

Author:

--*/

BOOL CSCardTestDlg::DestroyWindow()
{
// Unload COM
g_Wrapper.UnloadCOM();

//////////////////////////////////////////////////////////////////////
// Pass back through MFC
return CDialog::DestroyWindow();
}


SCARDDG.H

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    CSCardTestDlg

Abstract:

CSCardTestDlg object description file.

Author:

Environment:

    Win32, C++ w/Exceptions, COM/OLE

Revision History:

Notes:

--*/

#ifndef __SCARDTSTDLG_H__
#define __SCARDTSTDLG_H__

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//

/////////////////////////////////////////////////////////////////////////////
// CSCardTestDlg dialog

class CSCardTestDlg : public CDialog
{
// member Variables
private:

// Construction
public:
CSCardTestDlg(CWnd* pParent = NULL);// standard constructor

private:

// Dialog Data
//{{AFX_DATA(CSCardTestDlg)
enum { IDD = IDD_SCARDTST_DIALOG };
CEditm_editICCStatus;
CEditm_editComStatus;
CComboBoxm_cbRequestList;
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSCardTestDlg)
public:
virtual BOOL DestroyWindow();
protected:
virtual void DoDataExchange(CDataExchange* pDX);// DDX/DDV support
//}}AFX_VIRTUAL

protected:
// Implementation
HICON m_hIcon;

// Generated message map functions
//{{AFX_MSG(CSCardTestDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnPerformRequestButtonClick();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

#endif //__SCARDTSTDLG_H__


SCARDTST.CPP

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    SCardTestApp

Abstract:

CWinApp implementation file for the smart card test/example program. This
application implements a OLE container to test the smart card vendor
wrapper COM object and associated Smart Card resources.

Author:

Environment:

    Win32, C++ w/Exceptions, COM/OLE

Revision History:

Notes:

--*/

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//

#include "preapp.h"
#include "SCardTst.h"
#include "SCardDg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
//
// CSCardTestApp Message map
//

BEGIN_MESSAGE_MAP(CSCardTestApp, CWinApp)
//{{AFX_MSG_MAP(CSCardTestApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
//    DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

/*++

CSCardTestApp::CSCardTestApp()

Constructor for the Test application object

Arguments:

none

Return Value:

Author:

--*/

CSCardTestApp::CSCardTestApp()
{
/////////////////////////////////////////////////////////////////////////
// NOTE:
// Add any construction specific code here.
// Most significant initialization should be placed in InitInstance.
//
}

/////////////////////////////////////////////////////////////////////////////
//
//Create CCardTestApp
//

CSCardTestApp theApp;

/*++

CSCardTestApp::InitInstance()

Override to perform Windows instance initializatio for the dialog.

Arguments:

none

Return Value:

Nonzero if initialization is sucessful, zero (0) otherwise

Author:

--*/

BOOL CSCardTestApp::InitInstance()
{
// locals
BOOLbReturn = FALSE;

////////////////////////////////////////////////////////////////////////
//
// Allow ActiveX/COM within the dialog.
//
AfxEnableControlContainer();

/////////////////////////////////////////////////////////////////////////
//
// Standard initialization items
// NOTE:
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove those items which you
// do not need
//

#ifdef _AFXDLL
Enable3dControls();// Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic();// Call this when linking to MFC statically
#endif

////////////////////////////////////////////////////////////////////////
//
// Since this is a dialog based app, create the dialog object, save window
// handle, and open the dialog
//
CSCardTestDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();

/////////////////////////////////////////////////////////////////////////\
//
// Process the Dialog IDOK and IDCANCEL messages
//
if (nResponse == IDOK)
{
///////////////////////////////////////////////////////////////////
//
// Place code here to handle when the dialog is dismissed with OK
//
}
else if (nResponse == IDCANCEL)
{
/////////////////////////////////////////////////////////////////////
//
// Place code here to handle when the dialog is dismissed with Cancel
//
}

///////////////////////////////////////////////////////////////////////////
//
// Return value
//
return bReturn;
}


SCARDTST.H

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    SCardTestApp

Abstract:

CWinApp header/object description file for the smart card test/example program. This
application implements a OLE container to test the smart card vendor
wrapper COM object and associated Smart Card resources.

Author:

Environment:

    Win32, C++ w/Exceptions, COM/OLE

Revision History:

Notes:

--*/

#ifndef __SCARDTST_H__
#define __SCARDTST_H__

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//

#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif

#include "resapp.h"// main symbols

/////////////////////////////////////////////////////////////////////////////
//
// Class Declaration
//

class CSCardTestApp : public CWinApp
{
public:
CSCardTestApp();

/////////////////////////////////////////////////////////////////////////
//
// Overrides for CSCardTestApp
//
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSCardTestApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL

// Implementation

//////////////////////////////////////////////////////////////////////////
// CSCardTestApp Message Map
//
//{{AFX_MSG(CSCardTestApp)
// NOTE - the ClassWizard will add and remove member functions here.
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

#endif //__SCARDTST_H__


SCDTEST.CPP

/*++

Copyright (c) 1996-1997  Microsoft Corporation

Module Name:

scdtest

Abstract:

Simple brute force test SSP application. Usage: "test <# of iterations>".
Contains good examples for calling low-level SSPs.

Environment:

    Win32, C++ w/Exceptions, COM/OLE

Notes:

--*/

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//

#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <crtdbg.h>
#include <initguid.h>

//#include "sspmacro.h"
#include "scarddat.h"
#include "scardmgr.h"
#include "scardsrv.h"
#define IID_DEFINED
#include "scarddat_i.c"
#include "scardmgr_i.c"
#include "scardsrv_i.c"

/////////////////////////////////////////////////////////////////////////////
//
// Globals
//
LPSCARD g_pISCard = NULL;
LPSCARDDATABASE g_pISCardDatabase = NULL;
LPSCARDLOCATE g_pISCardLocate = NULL;
LPSCARDISO7816 g_pISCardISO7816 = NULL;
LPSCARDTYPECONV g_pISCardTypeConv = NULL;
LPSCARDCMD g_pISCardCmd = NULL;
LPBYTEBUFFER g_pIByteBuffer = NULL;

/////////////////////////////////////////////////////////////////////////////
//
// Macros
//

#ifndef SafeCast
#define SafeCast(cast,pt)((cast)(pt))
#endif

#ifndef ASSERT
#define ASSERT_ASSERTE
#endif

#ifndef IN
#define IN
#endif

////////////////////////////////////////////////////////////////////////////
//
// Defines
//

#define BYTE_BUFFER_HEAD(0)

/////////////////////////////////////////////////////////////////////////////
//
// Functions
//

/*++

ConTrace:

    Simply Tracing function to dump text to standard output.

Arguments:

lpszFormat - String to dump to standard output

--*/

void _cdecl
ConTrace(
IN LPCTSTR lpszFormat, ...)
{
//
// Helper to do print traces...
//

va_list args;
va_start(args, lpszFormat);

int nBuf;
TCHAR szBuffer[512];
ZeroMemory(szBuffer, SafeCast(DWORD,(512*sizeof(TCHAR))));

nBuf = _vstprintf(szBuffer, lpszFormat, args);
ASSERT(nBuf < sizeof(szBuffer));

_tprintf(szBuffer);
    OutputDebugString(szBuffer);
va_end(args);
}

/*++

DumpAPDU:

    Dumps the ReplyADPU of the current CardCmd object.

Arguments:

None.

--*/
void _cdecl
DumpAPDU( void )
{
#ifdef _DEBUG
// Locals
HRESULThresult = S_OK;
ULONGulBytesRead = 0;
BYTEpbyIStreamData[10];
LPBYTEBUFFER pBuffer = NULL;
TCHARpCharBuffer[10];
LARGE_INTEGERli;
STATSTRUCTstatstruct;

// Get the Command APDU
hresult = g_pISCardCmd->get_Apdu(&pBuffer);
if (FAILED(hresult)) {
ConTrace(_T("Failed to get command apdu."));
return;
};// if

// set seek position
LISet32 (li, BYTE_BUFFER_HEAD );
hresult = pBuffer->Seek((LONG) li.LowPart,
(LONG) STREAM_SEEK_SET,
NULL);
if (FAILED(hresult)){
ConTrace(_T("Unable to seek in IStream."));
return;
};
// Seek
hresult = pBuffer->Stat(&statstruct, (LONG) 0);
if (FAILED(hresult))
throw (hresult);

// Set buffer size
hresult = pBuffer->SetSize(statstruct.cbSize);
if (FAILED(hresult)) {
ConTrace(_T("Unable to set IStream size."));
return;
};

// Read Data
hresult = pBuffer->Read((BYTE*) pbyIStreamData,
(LONG) statstruct.cbSize,  // APDU header
(LONG*) &ulBytesRead);
if (FAILED(hresult)) {
ConTrace(_T("Unable to read IStream."));
return;
};

// "Quick" not best way to do this...
switch (statstruct.cbSize) {
case 5:
sprintf(pCharBuffer, "%d %d %d %d %d\n",
(int) pbyIStreamData[0], (int) pbyIStreamData[1],
(int) pbyIStreamData[2], (int) pbyIStreamData[3],
(int) pbyIStreamData[4]);
break;
case 4:
sprintf(pCharBuffer, "%d %d %d %d\n",
(int) pbyIStreamData[0], (int) pbyIStreamData[1],
(int) pbyIStreamData[2], (int) pbyIStreamData[3]);
break;
default:
// Sending data with command...Just display APDU.
sprintf(pCharBuffer, "%d %d %d %d %d\n",
(int) pbyIStreamData[0], (int) pbyIStreamData[1],
(int) pbyIStreamData[2], (int) pbyIStreamData[3],
(int) pbyIStreamData[4]);
break;
break;
 }; //switch

ConTrace( (LPCTSTR) pCharBuffer );

// Eliminate the temp buffer(s)...Clean up
if (pBuffer != NULL)
while (pBuffer->Release()>0);
#endif // _DEBUG
}

/*++

CallServer:

    Routine creates an instance of each low-level com object.

Arguments:

lpszFormat - String to dump to standard output

--*/
void _cdecl
CallServer(
void)
{
// locals
HRESULThresult;

ConTrace(_T("\nSTARTING Object Creation\n=============================\n"));
ConTrace(_T("Calling CoCreateInstance()...\n"));

hresult = CoCreateInstance(CLSID_CSCard,
NULL,
CLSCTX_ALL,
IID_ISCard,
(LPVOID*) &g_pISCard);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create CSCard object\n"));
return;
}
ConTrace(_T("Object CSCard created\n"));

hresult = CoCreateInstance(CLSID_CSCardTypeConv,
NULL,
CLSCTX_ALL,
IID_ISCardTypeConv,
(LPVOID*) &g_pISCardTypeConv);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create CSCardTypeConv object\n"));
return;
}
ConTrace(_T("Object CSCardTypeConv created\n"));

hresult = CoCreateInstance(CLSID_CSCardISO7816,
NULL,
CLSCTX_ALL,
IID_ISCardISO7816,
(LPVOID*) &g_pISCardISO7816);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create CSCardISO7816 object\n"));
return;
}
ConTrace(_T("Object CSCardISO7816 created\n"));

hresult = CoCreateInstance(CLSID_CSCardCmd,
NULL,
CLSCTX_ALL,
IID_ISCardCmd,
(LPVOID*) &g_pISCardCmd);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create CSCardCmd object\n"));
return;
}
ConTrace(_T("Object CSCardCmd created\n"));

hresult = CoCreateInstance(CLSID_CSCardLocate,
NULL,
CLSCTX_ALL,
IID_ISCardLocate,
(LPVOID*) &g_pISCardLocate);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create CSCardLocate object\n"));
return;
}
ConTrace(_T("Object CSCardLocate created\n"));

hresult = CoCreateInstance(CLSID_CSCardDatabase,
NULL,
CLSCTX_ALL,
IID_ISCardDatabase,
(LPVOID*) &g_pISCardDatabase);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create CSCardDatabase object\n"));
return;
}
ConTrace(_T("Object CSCardDatabase created\n"));

hresult = CoCreateInstance(CLSID_ByteBuffer,
NULL,
CLSCTX_ALL,
IID_IByteBuffer,
(LPVOID*) &g_pIByteBuffer);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create CSCardDatabase object\n"));
return;
}
ConTrace(_T("Object CByteBuffer created\n"));

ConTrace(_T("\nDONE!!! with Object Creation\n=============================\n"));
}

/*++

CallISOFunctions:

    Routine calls all the low level ISO build commands.

Arguments:

lpszFormat - String to dump to standard output

Notes:

1. This function does not actually send the command to a smartcard. It only
builds the apdu. In order to send the command to a card, the card/reader must be
attached:
hresult = g_pISCard->AttachByReader(bstrReaderName,
EXCLUSIVE,
T0);
if (FAILED(hresult)){
ConTrace(_T("Failed on AttachByReader\n"));
return;
}
Transactions must be performed:
hresult = g_pISCard->Transaction(&g_pISCardCmd);
And the card finally released.

--*/
void _cdecl
CallISOFunctions( longlLoops )
{
// Locals
HRESULThresult = S_OK;
LPBYTEBUFFERpBuffer1 = NULL,
pBuffer2 = NULL;
TCHARszBuffer[200];

ConTrace(_T("\nSTARTING Call Testing\n=============================\n"));
ConTrace(_T("Calling ISO Functions...\n"));

// Create a couple of byte buffers for testing
hresult = g_pISCardTypeConv->CreateByteBuffer((ULONG) 1,
&pBuffer1);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create IByteBuffer\n"));
return;
};// if
hresult = g_pISCardTypeConv->CreateByteBuffer((ULONG) 1,
&pBuffer2);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create IByteBuffer\n"));
return;
};// if

// Loop through the build/execute ISO commands
while (lLoops != 0) {
sprintf(szBuffer,"Iteration Number: %d\n", (int) lLoops);
ConTrace( (LPCTSTR) szBuffer );
// Appendrecord
ConTrace(_T("Build/Execute: AppendRecord\n"));
hresult = g_pISCardISO7816->AppendRecord(0,
pBuffer1,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on AppendRecord\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: AppendRecord successful\n"));

// EraseBinary
ConTrace(_T("Build/Execute: EraseBinary\n"));
hresult = g_pISCardISO7816->EraseBinary(0,
0,
pBuffer1,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on EraseBinary\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: EraseBinary successful\n"));

// ExternalAuthenticate
ConTrace(_T("Build/Execute: ExternalAuthenticate\n"));
hresult = g_pISCardISO7816->ExternalAuthenticate(0,
0,
pBuffer2,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on ExternalAuthenticate\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: ExternalAuthenticate successful\n"));

// GetChallenge
ConTrace(_T("Build/Execute: GetChallenge\n"));
hresult = g_pISCardISO7816->GetChallenge(10,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on GetChallenge\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: GetChallenge successful\n"));

// GetData
ConTrace(_T("Build/Execute: GetData\n"));
hresult = g_pISCardISO7816->GetData(0,
0,
10,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on GetData\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: GetData successful\n"));

// GetResponse
ConTrace(_T("Build/Execute: GetResponse\n"));
hresult = g_pISCardISO7816->GetResponse(0,
0,
0,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on GetResponse\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: GetResponse successful\n"));

// InternalAuthenticate
ConTrace(_T("Build/Execute: InternalAuthenticate\n"));
hresult = g_pISCardISO7816->InternalAuthenticate(0,
0,
pBuffer1,
10,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on InternalAuthenticate\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: InternalAuthenticate successful\n"));

// ManageChannel
ConTrace(_T("Build/Execute: ManageChannel\n"));
hresult = g_pISCardISO7816->ManageChannel(0x00,
0x80,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on ManageChannel\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: ManageChannel successful\n"));

// PutData
ConTrace(_T("Build/Execute: PutData\n"));
hresult = g_pISCardISO7816->PutData(0,
0,
pBuffer1,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on PutData\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: PutData successful\n"));

// ReadBinary
ConTrace(_T("Build/Execute: ReadBinary\n"));
hresult = g_pISCardISO7816->ReadBinary(0,
0,
20,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on ReadBinary\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: ReadBinary successful\n"));

// ReadRecord
ConTrace(_T("Build/Execute: ReadRecord\n"));
hresult = g_pISCardISO7816->ReadRecord(10,
5,
100,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on ReadRecord\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: ReadRecord successful\n"));

// SelectFile
ConTrace(_T("Build/Execute: SelectFile\n"));
hresult = g_pISCardISO7816->SelectFile(0,
0,
pBuffer1,
20,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on SelectFile\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: SelectFile successful\n"));

// SetDefaultClassID
ConTrace(_T("Build/Execute: SetDefaultClassID\n"));
hresult = g_pISCardISO7816->SetDefaultClassId(20);
if (FAILED(hresult)){
ConTrace(_T("Failed on SetDefaultClassID\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: SetDefaultClassID successful\n"));

// UpdateBinary
ConTrace(_T("Build/Execute: UpdateBinary\n"));
hresult = g_pISCardISO7816->UpdateBinary(0,
0,
pBuffer1,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on UpdateBinary\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: UpdateBinary successful\n"));

// UpdateRecord
ConTrace(_T("Build/Execute: UpdateRecord\n"));
hresult = g_pISCardISO7816->UpdateRecord(10,
1,
pBuffer2,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on UpdateRecord\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: UpdateRecord successful\n"));

// Verify
ConTrace(_T("Build/Execute: Verify\n"));
hresult = g_pISCardISO7816->Verify(1,
pBuffer2,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on Verify\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: Verify successful\n"));

// WriteBinary
ConTrace(_T("Build/Execute: WriteBinary\n"));
hresult = g_pISCardISO7816->WriteBinary(0,
0,
pBuffer1,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on WriteBinary\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: WriteBinary successful\n"));

// WriteRecord
ConTrace(_T("Build/Execute: WriteRecord\n"));
hresult = g_pISCardISO7816->WriteRecord(1,
1,
pBuffer1,
&g_pISCardCmd);
if (FAILED(hresult)){
ConTrace(_T("Failed on WriteRecord\n"));
//return;
}
DumpAPDU();
ConTrace(_T("Build/Execute: WriteRecord successful\n"));

// Decrement count
--lLoops;
};//while
// Clean up
if (pBuffer1 != NULL)
while (pBuffer1->Release());
if (pBuffer2 != NULL)
while (pBuffer2->Release());

ConTrace(_T("\nDONE!!! with Call Testing\n=============================\n"));
}

/*++

CallLocateFunctions:

    Calls the ISCardLocate functions (i.e. common dialog).

Arguments:

lpszFormat - String to dump to standard output

--*/
void _cdecl
CallLocateFunctions( long lLoops)
{
// Locals
HRESULThresult = S_OK;
LPSAFEARRAY rgCardName = NULL;
LPSAFEARRAY rgGroupName = NULL;
SCARDINFOscardinfo;
LPSCARDINFO pscardinfo = &scardinfo;
SAFEARRAYBOUND Bounds[] = {2,0},
   Bounds2[] = {1,0};
longlIndex[] = {0};// Access only 1 element
BSTRbstrCardName = ::SysAllocString(L"Cryptoflex"),
bstrCardName2 = ::SysAllocString(L"Sicrypt"),
bstrGroupName = ::SysAllocString(L"SCard$DefaultReaders");

ConTrace(_T("\nSTARTING Call Testing\n=============================\n"));
ConTrace(_T("Calling Locate Functions...\n"));

// Clear memory
::ZeroMemory((LPVOID) pscardinfo, (DWORD) sizeof(SCARDINFO) );

// Create a SAFEARRAY of type BSTR (contains
rgCardName = ::SafeArrayCreate(VT_BSTR,// BSTRs
(UINT) 1,// 1 dimensional array
Bounds);// That dimension contains 2 element

if (rgCardName == NULL) {
ConTrace(_T("\nUnable to create SAFEARRAY."));
return;
}
// Create a SAFEARRAY of type BSTR (contains
rgGroupName = ::SafeArrayCreate(VT_BSTR,// BSTRs
(UINT) 1,// 1 dimensional array
Bounds2);// That dimension contains 1 element

if (rgGroupName == NULL) {
ConTrace(_T("\nUnable to create SAFEARRAY."));
return;
}

// Add an element...
// lIndex is does not really have to be declared as an array since
// both SAFEARRAYs are 1 dimensional. An array is, however, required
// for multi dimensional arrays.
hresult = SafeArrayPutElement(rgCardName, lIndex, (LPVOID) bstrCardName);
if (FAILED(hresult)) {
ConTrace(_T("\nSafearray put element failed."));
return;
}
lIndex[0]= 1;
hresult = SafeArrayPutElement(rgCardName, lIndex, (LPVOID) bstrCardName2);
if (FAILED(hresult)) {
ConTrace(_T("\nSafearray put element failed."));
return;
}
lIndex[0] = 0;
hresult = SafeArrayPutElement(rgGroupName, lIndex, (LPVOID) bstrGroupName);
if (FAILED(hresult)) {
ConTrace(_T("\nSafearray put element failed."));
return;
}

// Setup for search
hresult = g_pISCardLocate->ConfigureCardNameSearch(rgCardName,
rgGroupName,
NULL,
0);
if (FAILED(hresult)) {
ConTrace(_T("\nConfigureCardSearch failed."));
return;
}

// Setup for common dialog..This actually displays the dialog for user input
//hresult = g_pISCardLocate->FindCard(bstrCardName,
//EXCLUSIVE,
//T0,
//&pscardinfo);
//if (FAILED(hresult)) {
//ConTrace(_T("\nFindCard failed."));
//return;
//}

ConTrace(_T("\nDONE!!! with Call Testing\n=============================\n"));

// Cleanup
if (rgCardName != NULL)
::SafeArrayDestroy(rgCardName);
if (rgGroupName != NULL)
::SafeArrayDestroy(rgGroupName);
if (bstrGroupName != NULL)
::SysFreeString(bstrGroupName);
if (bstrCardName != NULL)
::SysFreeString(bstrCardName);
if (bstrCardName != NULL)
::SysFreeString(bstrCardName2);
}

/*++

CallDbFunctions:

    Calls the ISCardDatabase functions.

Arguments:

lpszFormat - String to dump to standard output

--*/
void _cdecl
CallDbFunctions( long lLoops)
{
// Locals
GUIDguidProviderID;
LPGUIDlpguidProviderID = &guidProviderID;
HRESULThresult = S_OK;
BSTRbstrCardName = ::SysAllocString(L"Cryptoflex");
LPSAFEARRAYlpSafeArray = NULL;
LPBYTEBUFFER pBuffer = NULL;
LARGE_INTEGERli;
BYTEbAtr[] = {0x3b,0x00};
ULONGulBytesWritten = 0;

// GetProviderCardId
hresult = g_pISCardDatabase->GetProviderCardId(bstrCardName,
&lpguidProviderID);
if (FAILED(hresult)) {
ConTrace(_T("\nGetProviderCardId failed."));
}

//ListCardInterfaces...NOT SUPPORTED IN THIS RELEASE!!

// ListCards
// Create a bytebuffer for use in call
hresult = g_pISCardTypeConv->CreateByteBuffer((ULONG) 2,
&pBuffer);
if (FAILED(hresult)) {
ConTrace(_T("Failed to create IByteBuffer\n"));
};// if
// set seek position
LISet32 (li, BYTE_BUFFER_HEAD );
hresult = pBuffer->Seek((LONG) li.LowPart,
(LONG) STREAM_SEEK_SET,
NULL);
if (FAILED(hresult)){
ConTrace(_T("Unable to seek in IStream."));
};
// Fill in an ATR to search
hresult = pBuffer->Write((BYTE*) bAtr,
(LONG) 2,
(LONG*) &ulBytesWritten);
if (FAILED(hresult)) {
ConTrace(_T("Unable to write to IStream."));
};
// Call it
hresult = g_pISCardDatabase->ListCards(pBuffer,
NULL,
(long) 0,
&lpSafeArray);
if (FAILED(hresult)) {
ConTrace(_T("\nListCards failed."));
}
if (lpSafeArray != NULL) {
::SafeArrayDestroy(lpSafeArray);
lpSafeArray = NULL;
}

// ListReaderGroups
hresult = g_pISCardDatabase->ListReaderGroups((long) 0,
&lpSafeArray);
if (FAILED(hresult)) {
ConTrace(_T("\nListReaderGroups failed."));
}
if (lpSafeArray != NULL) {
::SafeArrayDestroy(lpSafeArray);
lpSafeArray = NULL;
}

// ListReaders
hresult = g_pISCardDatabase->ListReaders((long) 0,
&lpSafeArray);
if (FAILED(hresult)) {
ConTrace(_T("\nListReaders failed."));
}
if (lpSafeArray != NULL) {
::SafeArrayDestroy(lpSafeArray);
lpSafeArray = NULL;
}
}

/*++

Cleanup:

    Release all the interfaces/memory.

Arguments:

lpszFormat - String to dump to standard output

--*/
void _cdecl
CleanUp(
void)
{
while (g_pISCard->Release()>0);
while (g_pISCardISO7816->Release()>0);
while (g_pISCardTypeConv->Release()>0);
while (g_pISCardCmd->Release()>0);
// If required...
while (g_pISCardDatabase->Release()>0);
while (g_pISCardLocate->Release()>0);
while (g_pIByteBuffer->Release()>0);
}

/*++

main:

    Entry point.

Arguments:

lpszFormat - String to dump to standard output

--*/
int
main(
IN int argc,
IN char *argv[ ])
{
int nRet = 0;
long lIterations;

try
{
// Check command line arguments
if (argc==1)
lIterations = 1;
else if (argc==2)
lIterations = atol(argv[1]);
else {
printf ("\nCorrect Usage: test [#iterations]\n");
throw;
}

if (FAILED(CoInitialize(NULL)))
throw;

// Create Objects...
CallServer();
// Call Various Functions
CallISOFunctions(lIterations);
//CallLocateFunctions(lIterations);
//CallDbFunctions(lIterations);
// Clean Up...
CleanUp();

CoUninitialize();

#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif

nRet = 0;
}

catch (...)
{
printf(_T("Unhandled exception!!!\n"));
}

return (nRet);
}

/////////////////////////////////////////////////////////////////////////////


DLLDATAX.C

// wrapper for dlldata.c

#ifdef _MERGE_PROXYSTUB // merge proxy stub DLL

#define REGISTER_PROXY_DLL //DllRegisterServer, etc.

#pragma comment(lib, "rpcndr.lib")
#pragma comment(lib, "rpcns4.lib")
#pragma comment(lib, "rpcrt4.lib")

#define DllMainPrxDllMain
#define DllRegisterServerPrxDllRegisterServer
#define DllUnregisterServer PrxDllUnregisterServer
#define DllGetClassObject   PrxDllGetClassObject
#define DllCanUnloadNow     PrxDllCanUnloadNow

#include "dlldata.c"
#include "SCardDat_p.c"

#ifdef _NOPROXY //no midl generated dlldata.c

#define STRICT 1
#include <ole2.h>

BOOL WINAPI PrxDllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{return TRUE;}

STDAPI PrxDllCanUnloadNow(void){return S_OK;}

STDAPI PrxDllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{return CLASS_E_CLASSNOTAVAILABLE;}

STDAPI PrxDllRegisterServer(void){return S_OK;}

STDAPI PrxDllUnregisterServer(void){return S_OK;}

#endif //!PROXY_DELEGATION

#endif //_MERGE_PROXYSTUB


DLLDATAX.H

#ifdef _MERGE_PROXYSTUB

extern "C"
{
BOOL WINAPI PrxDllMain(HINSTANCE hInstance, DWORD dwReason,
LPVOID lpReserved);
STDAPI PrxDllCanUnloadNow(void);
STDAPI PrxDllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
STDAPI PrxDllRegisterServer(void);
STDAPI PrxDllUnregisterServer(void);
}

#endif


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by SCardCom.rc
//
#define IDS_PROJNAME                    100
#define IDS_SCARDATH_DESC               101
#define IDR_SCARDAUTH                   102
#define IDS_SCARDFIL_DESC               102
#define IDR_SCARDFILEACCESS             103
#define IDS_SCARDMAN_DESC               103
#define IDR_SCARDMANAGE                 104
#define IDS_SCARDVER_DESC               104
#define IDR_SCARDVERIFY                 105

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           106
#endif
#endif


SCARDATH.CPP

// SCardAth.cpp : Implementation of CSCardAuth

//////////////////////////////////////////////////////////////////////////
//
// Includes
//
#include "stdafx.h"
#include "SCardCom.h"
#include "SCardAth.h"
#include "SCardDef.h"

///////////////////////////////////////////////////////////////////////////
// Macros
//
#ifdef _DEBUG
#define TRACE_STR(name,sz) \
TRACE(_T("SCardCom.DLL: CSCardAuth::%s: %s\n"), name, sz)
#define TRACE_CODE(name,code) \
TRACE(_T("SCardCom.DLL: CSCardAuth::%s: error = 0x%x\n"), name, code)
#define TRACE_CATCH(name,code)TRACE_CODE(name,code)
#define TRACE_CATCH_UNKNOWN(name)TRACE_STR(name,_T("An unidentified exception has occurred!"))
#else
#define TRACE_STR(name,sz)((void)0)
#define TRACE_CODE(name,code)((void)0)
#define TRACE_CATCH(name,code)((void)0)
#define TRACE_CATCH_UNKNOWN(name)((void)0)
#endif  // _DEBUG

/////////////////////////////////////////////////////////////////////////////
// CSCardAuth

/*++

CSCardAuth::FinalConstruct:

    This routine defines a final constructor that is called after the constructor
for the template class is called.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

HRESULT CSCardAuth::FinalConstruct()
{
// Locals.
HRESULT hresult = S_OK;

try {
m_Manage = NULL;
}

catch (...) {
hresult = E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("GetChallenge"));
}

return (hresult);
}

/*++

CSCardAuth::FinalRelease:

    This routine defines a final release.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

HRESULT CSCardAuth::FinalRelease()
{
// Locals.
HRESULT hresult = S_OK;

try {
// Decrement the reference count on the "creation" object
if (m_Manage != NULL)
m_Manage->Release();
}

catch (...) {
}

return (hresult);
}

/////////////////////////////////////////////////////////////////////////////
//
// ISCardAuth Methods
//

/*++

CSCardAuth::APP_Auth

This method allows an application to athenticate itself to the ICC
using a Challange/signature protocol.

Arguments:

    lAlogID- specifies the algorithm to be used in the authentcation
process

pParam - IByteBuffer containing vendor specific parameters of the
authentication process

pBuffer - contains data required for authentication.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardAuth::APP_Auth(
long lAlgorID,
LPBYTEBUFFER pParam,
LPBYTEBUFFER pBuffer)
{
//locals
HRESULThresult = S_OK;
LPSCARDCMDpCmd = NULL;

try {
m_lAlgorID = lAlgorID;

// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

// Must have valid pointer...
if (pBuffer == NULL)
throw ( (HRESULT) E_POINTER );

// Create cmd object
hresult = m_Manage->CreateCmdObject(&pCmd);
if (FAILED(hresult))
throw (hresult);

// Do internal Authenticate
hresult = m_Manage->m_pISCardISO7816->ExternalAuthenticate((BYTE) lAlgorID,
NULL,
pBuffer,
&pCmd);
if (FAILED(hresult))
throw (hresult);
// Force correct class id
hresult = pCmd->put_ClassId(VENDOR_CLASS_ID);
if (FAILED(hresult))
throw (hresult);

hresult = m_Manage->m_pISCard->LockSCard();
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->Transaction(&pCmd);
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->UnlockSCard(LEAVE);
if (FAILED(hresult))
throw(hresult);

//interpret results and return
hresult = pCmd->get_ApduReplyLength(&m_lReplyLength);
if (FAILED(hresult))
throw (hresult);
hresult = pCmd->get_ReplyStatus( &m_wReplyStatus );
if (FAILED(hresult))
throw (hresult);
if ( m_wReplyStatus != 0x9000)
throw (E_FAIL);
}

catch (HRESULT hr) {
hresult=hr;
TRACE_CATCH(_T("APP_Auth"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("APP_AUTH"));
}

// Release cmd interface
if (pCmd != NULL)
pCmd->Release();

return hresult;
}

/*++

CSCardAuth::GetChallenge

Returns a challenge from the ICC.

Arguments:

    lAlgorID - specifies the algorithm the challenge will be used with.

pParam - Contains vendor specific parameters used in the
authentication process.

lpLengthOfChallenge - indicates length of ppBuffer

ppBuffer - contains the challenge to be used in subsequent
application authentication

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardAuth::GetChallenge(
LONG lAlgorID,
LPBYTEBUFFER pParam,
LONG* lpLengthOfChallenge,
LPBYTEBUFFER *ppBuffer)
{
//locals
HRESULThresult = S_OK;
LPSCARDCMDpCmd = NULL;

try {
m_lAlgorID = lAlgorID;

// CheckLength
if (*lpLengthOfChallenge<0)
throw ( (HRESULT) E_INVALIDARG );

// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

// Check pointer...Will create it later if needed.
if (ppBuffer == NULL)
throw ( (HRESULT) E_POINTER );

// Create a command object
hresult = m_Manage->CreateCmdObject(&pCmd);
if (FAILED(hresult))
throw (hresult);

// Do challenge
hresult = m_Manage->m_pISCardISO7816->GetChallenge(*lpLengthOfChallenge,
&pCmd);
if (FAILED(hresult))
throw (hresult);
// Force correct class id for the the CryptoFlex card
hresult = pCmd->put_ClassId(VENDOR_CLASS_ID);
if (FAILED(hresult))
throw (hresult);

hresult = m_Manage->m_pISCard->LockSCard();
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->Transaction(&pCmd);
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->UnlockSCard(LEAVE);
if (FAILED(hresult))
throw(hresult);

//interpret results and return
hresult = pCmd->get_ApduReplyLength(&m_lReplyLength);
if (FAILED(hresult))
throw (hresult);
hresult = pCmd->get_ReplyStatus( &m_wReplyStatus );
if (FAILED(hresult))
throw (hresult);
if ( m_wReplyStatus != 0x9000)
throw (E_FAIL);
hresult = pCmd->get_ApduReply( ppBuffer );
if (FAILED(hresult))
throw (hresult);
}

catch (HRESULT hr) {
hresult = hr;
TRACE_CATCH(_T("GetChallenge"),hr);
}

catch (...) {
hresult = E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("GetChallenge"));
}

// Release cmd interface
if (pCmd != NULL)
pCmd->Release();

return hresult;
}

/*++

CSCardAuth::ICC_Auth

Allows application to authenticate the ICC

Arguments:

    lAlgorID - specifies the algorithm to be used in the authentication
process.

pParam - Contains vendor specific parameters used in the
authentication process.

  lpLength - contains the length of the valid data in ppBuffer.

ppBuffer - (in) cotains the data to be used in the authentication process;
(out) contins the result of the signation process

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardAuth::ICC_Auth(
long lAlgorID,
LPBYTEBUFFER pParam,
LONG* lpLength,
LPBYTEBUFFER *ppBuffer)
{
//locals
HRESULThresult = S_OK;
LPSCARDCMDpCmd = NULL;

try {
m_lAlgorID = lAlgorID;

// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

// Must have valid pointer...
if (ppBuffer == NULL)
throw ( (HRESULT) E_POINTER );

// create buffer if needed
if ( (*ppBuffer) == NULL) {
hresult = m_Manage->m_pISCardTypeConv->CreateByteBuffer((ULONG) 0,
ppBuffer);
if (FAILED(hresult))
throw (hresult);
};// if

// Create Cmd Object
hresult = m_Manage->CreateCmdObject(&pCmd);
if (FAILED(hresult))
throw (hresult);

// Do internal Authenticate
hresult = m_Manage->m_pISCardISO7816->InternalAuthenticate((BYTE) lAlgorID,
NULL,
(*ppBuffer),
0,
&pCmd);
if (FAILED(hresult))
throw (hresult);
// Force correct class id for the the CryptoFlex card
hresult = pCmd->put_ClassId(VENDOR_CLASS_ID);
if (FAILED(hresult))
throw (hresult);

hresult = m_Manage->m_pISCard->LockSCard();
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->Transaction(&pCmd);
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->UnlockSCard(LEAVE);
if (FAILED(hresult))
throw(hresult);

//interpret results and return
hresult = pCmd->get_ApduReplyLength(&m_lReplyLength);
if (FAILED(hresult))
throw (hresult);
hresult = pCmd->get_ReplyStatus( &m_wReplyStatus );
if (FAILED(hresult))
throw (hresult);
if ( m_wReplyStatus != 0x9000)
throw (E_FAIL);
hresult = pCmd->get_ApduReply( ppBuffer );
if (FAILED(hresult))
throw (hresult);
}

catch (HRESULT hr) {
hresult=hr;
TRACE_CATCH(_T("ICC_Auth"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("ICC_AUTH"));
}

// Release cmd interface
if (pCmd != NULL)
pCmd->Release();

return hresult;
}

/*++

CSCardAuth::Initialize

Initializes the object for use.

Arguments:

lp - a long pointer to the "controlling" ISCardManage object.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardAuth::Initialize( LONG *lp)
{
// Locals
HRESULThresult = S_OK;

try {
// Check Params, etc..
if (lp == NULL)
throw ( (HRESULT) E_INVALIDARG );

if (m_Manage != NULL)
throw ( (HRESULT) E_FAIL );

// Ok...
m_Manage = (LPCSCARDMANAGE) lp;
}

catch (HRESULT hr) {
hresult=hr;
TRACE_CATCH(_T("Initialize"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("Initialize"));
}

return (hresult);
}

/*++

CSCardAuth::User_Auth

Allows a remote app to authenticate the Client

Arguments:

    lAlgorID - specifies the algorithm to be used in the authentication
process.

pParam - Contains vendor specific parameters used in the
authentication process.

lpLength - contains the length of the valid data in pBuffer.

ppBuffer - contains the challenge data on input and the authentication
data on output.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardAuth::User_Auth(
LONG lAlgorID,
LPBYTEBUFFER pParam,
LONG* lpLength,
LPBYTEBUFFER* ppBuffer)
{
//locals
HRESULThresult = E_NOTIMPL;

try {
// Todo: Add code if ICC supports
}

catch (HRESULT hr) {
hresult=hr;
TRACE_CATCH(_T("User_Auth"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("USER_AUTH"));
}

return hresult;
}


SCARDATH.H

// SCardAth.h : Declaration of the CSCardAuth

#ifndef __SCARDAUTH_H_
#define __SCARDAUTH_H_

#include "resource.h"       // main symbols
#include "scardman.h"

/////////////////////////////////////////////////////////////////////////////
// CSCardAuth
class ATL_NO_VTABLE CSCardAuth :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CSCardAuth, &CLSID_SCardAuth>,
public IDispatchImpl<ISCardAuth, &IID_ISCardAuth, &LIBID_SCARDCOMLib>
{
private:
// Members
LPCSCARDMANAGEm_Manage;
longm_lAlgorID;
LONGm_lReplyLength;
WORDm_wReplyStatus;

public:
CSCardAuth(){}

~CSCardAuth(){}

HRESULT FinalConstruct();

HRESULT FinalRelease();

//
//DECLARE_NOT_AGGREGATABLE(CSCardFileAccess)
// Note:
// Remove the comment from the line above if you don't want your object to
// support aggregation.  The default is to support it
//

//Registry:

//Simple ALL 1.0 based registry entry
DECLARE_REGISTRY(CSCardAuth,
_T("SCardCOM.SCardAuth.1"),
_T("SCardCOM.SCardAuth"),
IDS_SCARDATH_DESC,
THREADFLAGS_APARTMENT)
// To use script base registration use the line below
// DECLARE_REGISTRY_RESOURCEID(IDR_SCARDAUTH)

BEGIN_COM_MAP(CSCardAuth)
COM_INTERFACE_ENTRY(ISCardAuth)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// ISCardAuth
private:

public:
STDMETHOD(APP_Auth)(
LONG lAlgorID,
LPBYTEBUFFER pParam,
LPBYTEBUFFER pBuffer);
STDMETHOD(GetChallenge)(
LONG lAlgorID,
LPBYTEBUFFER pParam,
LONG* lpLengthOfChallenge,
LPBYTEBUFFER *ppBuffer);
STDMETHOD(ICC_Auth)(
LONG lAlgorID,
LPBYTEBUFFER pParam,
LONG* lpLength,
LPBYTEBUFFER *ppBuffer);
STDMETHOD(Initialize)(
LONG* lp);
STDMETHOD(User_Auth)(
LONG lAlgorID,
LPBYTEBUFFER pParam,
LONG* lpLength,
LPBYTEBUFFER *ppBuffer);
};

#endif //__SCARDAUTH_H_


SCARDCOM.CPP

// SCardCom.cpp : Implementation of DLL Exports.

// Note: Proxy/Stub Information
//To build a separate proxy/stub DLL,
//run nmake -f SCardComps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"
#include "SCardCom.h"

#include "SCardCom_i.c"
#include "SCardAth.h" #include "SCardFil.h" #include "SCardMan.h" #include "SCardVer.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_SCardAuth, CSCardAuth)
OBJECT_ENTRY(CLSID_SCardFileAccess, CSCardFileAccess)
OBJECT_ENTRY(CLSID_SCardManage, CSCardManage)
OBJECT_ENTRY(CLSID_SCardVerify, CSCardVerify)
END_OBJECT_MAP()

class CSCardComApp : public CWinApp
{
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
};

CSCardComApp theApp;

BOOL CSCardComApp::InitInstance()
{
_Module.Init(ObjectMap, m_hInstance);
return CWinApp::InitInstance();
}

int CSCardComApp::ExitInstance()
{
_Module.Term();
return CWinApp::ExitInstance();
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return (AfxDllCanUnloadNow()==S_OK && _Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
_Module.UnregisterServer();
return S_OK;
}


SCARDCOM.IDL

// SCardCom.idl : IDL source for SCardCom.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (SCardCom.tlb) and marshalling code.

#ifndef DO_NO_IMPORTS
    import "SspsIdl.idl";
    import "ocidl.idl";
#endif

interface ISCardAuth;
interface ISCardFileAcess;
interface ISCardManage;
interface ISCardVerify;

cpp_quote("")
cpp_quote("//////////////////////////////////////////////////////////////////")
cpp_quote("//")
cpp_quote("// Types and Constants")
cpp_quote("//")

cpp_quote("#ifndef _HSCARD_FILE")
    cpp_quote("#define _HSCARD_FILE")
    typedef LONG HSCARD_FILE;
cpp_quote("#endif // _HSCARD_FILE")

cpp_quote("#ifndef _HSCARD_DEFINED")
    cpp_quote("#define _HSCARD_DEFINED")
    typedef LONG HSCARD;
cpp_quote("#endif")

cpp_quote("#ifndef _NULL_DEFINED")
    cpp_quote("#define _NULL_DEFINED")
    #define NULL    0
cpp_quote("#endif // !_NULL_DEFINED")

cpp_quote("#ifndef _LPBYTE_DEFINED")
    cpp_quote("#define _LPBYTE_DEFINED")
    typedef [public] BYTE *LPBYTE;
cpp_quote("#endif // !_LPBYTE_DEFINED")

cpp_quote("#ifndef _LPCBYTE_DEFINED")
    cpp_quote("#define _LPCBYTE_DEFINED")
    typedef [public] const BYTE *LPCBYTE;
cpp_quote("#endif // !_LPCBYTE_DEFINED")

cpp_quote("#ifndef _ISO_ERROR_SUCCESS")
    cpp_quote("#define _ISO_ERROR_SUCCESS")
    #define ISO_SUCCESS 0x9000
    cpp_quote("#define ISO_SUCCESS      (0x9000)")
cpp_quote("#endif // !_ISO_ERROR_SUCCESS")

cpp_quote("#ifndef _MAX_PATH_LEN_DEFINED")
    cpp_quote("#define _MAX_PATH_LEN_DEFINED")
    #define MAX_PATH_LEN    256
    cpp_quote("#define MAX_PATH_LEN     (256)")
cpp_quote("#endif // !_MAX_PATH_LEN_DEFINED")

cpp_quote("#ifndef _MAX_NAME_LEN_DEFINED")
    cpp_quote("#define _MAX_NAME_LEN_DEFINED")
    #define MAX_NAME_LEN    16
    cpp_quote("#define MAX_NAME_LEN     (16)")
cpp_quote("#endif // !_MAX_NAME_LEN_DEFINED")

cpp_quote("#ifndef _MAX_WRITE_LEN_DEFINED")
    cpp_quote("#define _MAX_WRITE_LEN_DEFINED")
    #define MAX_WRITE_LEN   255
    cpp_quote("#define MAX_WRITE_LEN        (255)")
cpp_quote("#endif // !_MAX_WRITE_LEN_DEFINED")

cpp_quote("#ifndef _MAX_READ_LEN_DEFINED")
    cpp_quote("#define _MAX_READ_LEN_DEFINED")
    #define MAX_READ_LEN    250
    cpp_quote("#define MAX_READ_LEN     (250)")
cpp_quote("#endif // !_MAX_READ_LEN_DEFINED")

cpp_quote("#ifndef _SC_TLV_VALID_DEFINED")
    cpp_quote("#define _SC_TLV_VALID_DEFINED")
    #define SC_TLV_VALID    1
    cpp_quote("#define SC_TLV_VALID     (1)")
cpp_quote("#endif // !_SC_TLV_VALID_DEFINED")

cpp_quote("#ifndef _SC_TLV_NOT_VALID_DEFINED")
    cpp_quote("#define _SC_TLV_NOT_VALID_DEFINED")
    #define SC_TLV_NOT_VALID    0
    cpp_quote("#define SC_TLV_NOT_VALID     (0)")
cpp_quote("#endif // !_SC_TLV_NOT_VALID_DEFINED")

cpp_quote("#ifndef _REFTYPE_DEFINED")
    cpp_quote("#define _REFTYPE_DEFINED")
    typedef [helpstring("Smart Card File Access Reference Types"),
             //uuid(),
             //helpcontext(),
             v1_enum,   // force 32-bit transmission over RPC, if in use...
             version(1.0)]
    enum tagREFTYPE {
        SC_TYPE_BY_NAME,
        SC_TYPE_BY_ID,
        SC_TYPE_BY_SHORT_ID
     } REFTYPE;
cpp_quote("#endif // !_REFTYPE_DEFINED")

cpp_quote("#ifndef _FILETYPE_DEFINED")
    cpp_quote("#define _FILETYPE_DEFINED")
    typedef [helpstring("Smart Card File Access File Types"),
             //uuid(),
             //helpcontext(),
             v1_enum,   // force 32-bit transmission over RPC, if in use...
             version(1.0)]
    enum tagFILETYPE {
        SC_TYPE_DIRECTORIES,
        SC_TYPE_FILES,
        SC_TYPE_ALL_FILES,
        SC_TYPE_DIRECTORY_FILE,
        SC_TYPE_TRANSPARENT_EF,
        SC_TYPE_FIXED_EF,
        SC_TYPE_CYCLIC_EF,
        SC_TYPE_VARIABLE_EF
     } FILETYPE;
cpp_quote("#endif // !_FILETYPE_DEFINED")

cpp_quote("#ifndef _SEEKTYPE_DEFINED")
    cpp_quote("#define _SEEKTYPE_DEFINED")
    typedef [helpstring("Smart Card File Access Seek Types"),
             //uuid(),
             //helpcontext(),
             v1_enum,   // force 32-bit transmission over RPC, if in use...
             version(1.0)]
    enum tagSEEKTYPE {
        SC_SEEK_FROM_BEGINNING,
        SC_SEEK_FROM_END,
        SC_SEEK_RELATIVE
     } SEEKTYPE;
cpp_quote("#endif // !_SEEKTYPE_DEFINED")

cpp_quote("#ifndef _SCARD_FLAGS_DEFINED")
    cpp_quote("#define _SCARD_FLAGS_DEFINED")
    typedef [helpstring("Smart Card File/Verification Flags"),
             //uuid(),
             //helpcontext(),
             v1_enum,   // force 32-bit transmission over RPC, if in use...
             version(1.0)]
    enum tagSCARD_FLAGS {
        SC_FL_SECURE_MESSAGING = 0x01,
        SC_FL_REPLACE = 0x02,
        SC_FL_ERASE = 0x04,
        SC_FL_OR = 0x08,
        SC_FL_AND = 0x10,
        SC_FL_RECURSIVE = 0x20,
        SC_FL_NON_RECURSIVE = 0x40,
        SC_FL_IHV_GLOBAL = 0x80,
        SC_FL_IHV_LOCAL = 0x100,
        SC_FL_IHV_ENABLE = 0x200,
        SC_FL_IHV_DISABLE = 0x400,
        SC_FL_IHV_CHANGE = 0x800,
        SC_FL_PREALLOCATE = 0x1000,
        SC_FL_GET_ALL_PROPERTIES = 0x2000,
        SC_FL_STAT_DEFAULT = 0x4000
     } SCARD_FLAGS;
cpp_quote("#endif // !_SCARD_FLAGS_DEFINED")

cpp_quote("#ifndef _SCARD_SHARE_MODES_DEFINED")
    cpp_quote("#define _SCARD_SHARE_MODES_DEFINED")
    typedef [helpstring("SCard Share Modes"),
             //uuid(),
             //helpcontext(),
             v1_enum,   // force 32-bit transmission over RPC, if in use...
             version(1.0)]
    enum tagSCARD_SHARE_MODES {
        EXCLUSIVE = 1,
        SHARED = 2
     } SCARD_SHARE_MODES;
cpp_quote("#endif // !_SCARD_SHARE_MODES_DEFINED")

cpp_quote("#ifndef _SCARD_DISPOSITIONS_DEFINED")
    cpp_quote("#define _SCARD_DISPOSITIONS_DEFINED")
    typedef [helpstring("SCard Dispositions"),
             //uuid(),
             //helpcontext(),
             v1_enum,   // force 32-bit transmission over RPC, if in use...
             version(1.0)]
    enum tagSCARD_DISPOSITIONS {
        LEAVE = 0,
        RESET = 1,
        UNPOWER = 2,
        EJECT = 3
     } SCARD_DISPOSITIONS;
cpp_quote("#endif // !_SCARD_DISPOSITIONS_DEFINED")

cpp_quote("#ifndef _TLV_TABLE_DEFINED")
    cpp_quote("#define _TLV_TABLE_DEFINED")
    typedef [helpstring("TLV Structures"),
             //uuid(),
             //helpcontext(),
             version(1.0)]
    struct tagTLV_TABLE {
        LONG cbSize;
        [size_is(cbSize)] BYTE byData[];
     } TLV_TABLE;

    cpp_quote("#define _CB_TLV_TABLE_DEFINED")
    cpp_quote("#define CB_TLV_TABLE     (sizeof(TLV_TABLE))")

    cpp_quote("#define _PTLV_TABLE_DEFINED")
    typedef [unique] TLV_TABLE *PTLV_TABLE;

    cpp_quote("#define _PCTLV_TABLE_DEFINED")
    typedef [unique] const TLV_TABLE *PCTLV_TABLE;

    cpp_quote("#define _LPTLV_TABLE_DEFINED")
    typedef [unique] TLV_TABLE *LPTLV_TABLE;

    cpp_quote("#define _LPCTLV_TABLE_DEFINED")
    typedef [unique] const TLV_TABLE *LPCTLV_TABLE;
cpp_quote("#endif // !_TLV_TABLE_DEFINED")

cpp_quote("#ifndef _SCARD_STATES_DEFINED")
    cpp_quote("#define _SCARD_STATES_DEFINED")
    typedef [helpstring("SCard States"),
             //uuid(),
             //helpcontext(),
             v1_enum,   // force 32-bit transmission over RPC, if in use...
             version(1.0)]
    enum tagSCARD_STATES {
        ABSENT = 1,
        PRESENT = 2,
        SWALLOWED = 3,
        POWERED = 4,
        NEGOTIABLEMODE = 5,
        SPECIFICMODE = 6
     } SCARD_STATES;
cpp_quote("#endif // !_SCARD_STATES_DEFINED")

cpp_quote("#ifndef _SCARD_PROTOCOLS_DEFINED")
    cpp_quote("#define _SCARD_PROTOCOLS_DEFINED")
    typedef [helpstring("SCard Protocols"),
             //uuid(),
             //helpcontext(),
             v1_enum,   // force 32-bit transmission over RPC, if in use...
             version(1.0)]
    enum tagSCARD_PROTOCOLS {
        T0 = 0x01,  // T=0 is the active protocol.
        T1 = 0x02,  // T=1 is the active protocol.
        RAW = 0xff  // Raw is the active protocol.
     } SCARD_PROTOCOLS;
cpp_quote("#endif // !_SCARD_PROTOCOLS_DEFINED")

cpp_quote("#ifndef _FILESPEC_DEFINED")
    cpp_quote("#define _FILESPEC_DEFINED")
    typedef [helpstring("File specification structure."),
             //uuid(),
             //helpcontext(),
             version(1.0)]
    struct tagFILESPEC {
        WORD wFileID;
        BYTE byFilename[MAX_NAME_LEN];
        BYTE byShortID;
     } FILESPEC;

    cpp_quote("#define _CB_FILESPEC_DEFINED")
    cpp_quote("#define CB_FILESPEC  (sizeof(FILESPEC))")

    cpp_quote("#define _PFILESPEC_DEFINED")
    typedef [public] FILESPEC *PFILESPEC;

    cpp_quote("#define _PCFILESPEC_DEFINED")
    typedef [public] const FILESPEC *PCFILESPEC;

    cpp_quote("#define _LPFILESPEC_DEFINED")
    typedef [public] FILESPEC *LPFILESPEC;

    cpp_quote("#define _LPCFILESPEC_DEFINED")
    typedef [public] const FILESPEC *LPCFILESPEC;
cpp_quote("#endif // _FILESPEC_DEFINED")

cpp_quote("")
cpp_quote("//////////////////////////////////////////////////////////////////")
cpp_quote("//")
cpp_quote("// ISCardAuth Interface")
cpp_quote("//")

cpp_quote("#ifndef _LPSCARDAUTH_DEFINED")
cpp_quote("#define _LPSCARDAUTH_DEFINED")
    [
        object,
        uuid(7B063D61-6E40-11d0-B858-00C04FD424B9),
        dual,
        helpstring("ISCardAuth Interface"),
        pointer_default(unique)
    ]
    interface ISCardAuth : IDispatch
    {
        // Includes -

        import "oaidl.idl";

        // Pointer(s) -

        typedef [unique] ISCardAuth* LPSCARDATH;
        typedef [unique] LPSCARDATH LPSCARDAUTH;

        // Properties -

        // Methods -
        [id(0),
         helpstring("Allows application to authenticate itself to the ICC")]
        HRESULT APP_Auth(   [in]    LONG lAlgoID,
                            [in]    LPBYTEBUFFER pParam,
                            [in]    LPBYTEBUFFER pBuffer);

        [id(1),
         helpstring("Returns a challenge from ICC ")]
        HRESULT GetChallenge(   [in, optional]  LONG lAlgoID,
[in]    LPBYTEBUFFER pParam,
[in,out]    LONG* lpLengthOfChallenge,
[in,out]    LPBYTEBUFFER* ppBuffer);

        [id(2),
         helpstring("Allows application to authenticate the ICC")]
        HRESULT ICC_Auth(   [in]    LONG lAlgoID,
[in]    LPBYTEBUFFER pParam,
[in,out] LONG* lpLength,
[in,out] LPBYTEBUFFER* ppBuffer);

        [id(3),
         helpstring("Called to initialize the object. This is an internal ONLY use function."),
         hidden]
        HRESULT Initialize( LONG* lp );

        [id(4),
         helpstring("Allows a remote app to authenticate the Client")]
        HRESULT User_Auth(  [in]    LONG lAlgoID,
[in]    LPBYTEBUFFER pParam,
[in,out]    LONG* lpLength,
[in,out]    LPBYTEBUFFER* ppBuffer);

    };
cpp_quote("#endif // !_LPSCARDAUTH_DEFINED")

cpp_quote("")
cpp_quote("//////////////////////////////////////////////////////////////////")
cpp_quote("//")
cpp_quote("// ISCardFileAccess Interface Definition")
cpp_quote("//")

cpp_quote("#ifndef _LPSCARDFILEACCESS_DEFINED")
cpp_quote("#define _LPSCARDFILEACCESS_DEFINED")
    [
        object,
        uuid(4029DD8A-5902-11D0-B84C-00C04FD424B9),
        dual,
        helpstring("ISCardFileAccess Interface"),
        pointer_default(unique)
    ]
    interface ISCardFileAccess : IDispatch
    {
        // Include(s) -

        import "oaidl.idl";

        // Pointer(s) -
        typedef [unique] ISCardFileAccess* LPSCARDFILE;
        typedef [unique] LPSCARDFILE LPSCARDFILEACCESS;

        // Method(s) -

        [id(0),
         helpstring("Change the currently selected directory to the specified directory")]
        HRESULT ChangeDir([in] REFTYPE refType,
                          [in] BSTR bstrNewDir);

        [id(1),
         helpstring("Closes the specified file")]
        HRESULT Close([in] HSCARD_FILE hFile);

        [id(2),
         helpstring("Create a file of the specified type at a specified location")]
        HRESULT Create([in] REFTYPE refType,
                       [in] BSTR bstrPathSpec,
                       [in,out] LPTLV_TABLE* ppTLVs,
   [in,out] LONG* lpcTLVs,
                       [in] SCARD_FLAGS flags,
                       [in,defaultvalue(NULL)] LPBYTEBUFFER pDataBuffer);

        [id(3),
         helpstring("Deletes the specified file")]
        HRESULT Delete([in] REFTYPE refType,
                       [in] BSTR bstrPathSpec,
                       [in] SCARD_FLAGS flags);

        [id(4),
         helpstring("Retrieve a list of the FileSpecs of the specified type")]
        HRESULT Directory([in] FILETYPE fileType,
                          [out, retval] LPSAFEARRAY *ppFileList);   // SAFEARRAY of BSTRs..

        [id(5),
         helpstring("Retrieve the absolute path to the currently selected directory")]
        HRESULT GetCurrentDir([out, retval] BSTR* pbstrPathSpec);

        [id(6),
         helpstring("Retrieve list of file capabilites.")]
        HRESULT GetFileCapabilities([in,out] LPTLV_TABLE* ppProperties,
                                    [in,out] LONG* plProperties,
                                    [in] SCARD_FLAGS Flags);

        [id(7),
         helpstring("Retrieve the primitive data referred by tags (TLV) for the specified object")]
        HRESULT GetProperties([in] REFTYPE refType,
                              [in] BSTR bstrPathSpec,
                              [in,out] LPTLV_TABLE* ppTLV,
  [in,out] LONG* plcTLV,
                              [in] SCARD_FLAGS flags);

        [id(8),
         helpstring("Called to initialize the object. This is an internal ONLY use function."),
         hidden]
        HRESULT Initialize( LONG* lp );

        [id(9),
         helpstring("Invalidate the specified file (EF or DF)")]
        HRESULT Invalidate([in] BSTR bstrPathSpec,
                           [in] SCARD_FLAGS flags);

        [id(10),
         helpstring("Opens the specified file for further use")]
        HRESULT Open([in] REFTYPE refType,
                     [in] BSTR bstrPathSpec,
                     [out, retval] HSCARD_FILE* phFile);

        [id(11),
         helpstring("Read data from the specified file")]
        HRESULT Read([in] HSCARD_FILE hFile,
                     [in,out] LONG *lBytesToRead,
                     [out] LPBYTEBUFFER* ppBuffer,
                     [in] SCARD_FLAGS flags);

        [id(12),
         helpstring("Rehabilitate the specified file (EF or DF)")]
        HRESULT Rehabilitate([in] BSTR bstrPathSpec,
                             [in] SCARD_FLAGS flags);

        [id(13),
         helpstring("Selects the object from which access (read or write) will be done")]
        HRESULT Seek([in] HSCARD_FILE hFile,
                     [in] LONG lOffset,
                     [in] SEEKTYPE seek);

        [id(14),
         helpstring("Set the primitive data referred by tags (TLV) for the specified object (file or directory)")]
        HRESULT SetProperties([in] REFTYPE refType,
                              [in] BSTR bstrPathSpec,
                              [in] LPTLV_TABLE pTLV,
  [in] LONG lcTLV,
                              [in] SCARD_FLAGS flags);

        [id(15),
         helpstring("Write data to the specified file")]
        HRESULT Write([in] HSCARD_FILE hFile,
[in] LONG lLength,
[in] LPBYTEBUFFER pData,
[in] SCARD_FLAGS flags);

    };
cpp_quote("#endif // !_LPSCARDFILEACCESS_DEFINED")

cpp_quote("")
cpp_quote("//////////////////////////////////////////////////////////////////")
cpp_quote("//")
cpp_quote("// ISCardMange Interface")
cpp_quote("//")

cpp_quote("#ifndef _LPSCARDMANAGE_DEFINED")
cpp_quote("#define _LPSCARDMANAGE_DEFINED")
    [
        object,
        uuid(5E586211-5A09-11d0-B84C-00C04FD424B9),
        dual,
        helpstring("ISCardManage Interface"),
        pointer_default(unique)
    ]
    interface ISCardManage : IDispatch
    {
        // Includes -

        import "oaidl.idl";

        // Pointer(s) -

        typedef [unique] ISCardManage* LPSCARDMAN;
        typedef [unique] LPSCARDMAN LPSCARDMANAGE;

        // Properties -

        // Methods -
        [id(0),
         helpstring("Allows application to start communication with an ICC")]
        HRESULT AttachByHandle([in] HSCARD hICC);

        [id(1),
         helpstring("Allows application request establishment of a context for a specific IFD.")]
        HRESULT AttachByIFD([in] BSTR bstrIFDName,
[in] SCARD_SHARE_MODES Flags );

        [id(2),
         helpstring("Creates instance of ISCardFileAccess interface.")]
         HRESULT CreateFileAccess( ISCardFileAccess** ppISCardFileAccess );

        [id(3),
         helpstring("Creates instance of ISCardAuth interface.")]
         HRESULT CreateCardAuth( ISCardAuth** ppISCardAuth );

        [id(4),
         helpstring("Creates instance of ISCardVerify interface.")]
         HRESULT CreateCHVerification( ISCardVerify** ppISCardVerify );

        [id(5),
         helpstring("Release the attachment to a particular ICC or IFD.")]
        HRESULT Detach();

        [id(6),
         helpstring("Lock for exclusive use the connected IFD/ICC.")]
        HRESULT Lock();

        [id(7),
         helpstring("Allows application to reconnect to an ICC or IFD.")]
        HRESULT Reconnect();

        [id(8),
         helpstring("Get status of ICC/IFD.")]
        HRESULT Status( [out]   SCARD_STATES *pStatus,
                        [out]   SCARD_PROTOCOLS *pProtocol);

        [id(9),
         helpstring("Unlock/release exclusive use of the connected IFD/ICC.")]
        HRESULT Unlock([in, defaultvalue(LEAVE)]
SCARD_DISPOSITIONS Disposition);

        // New interfaces
        [id(10),
         helpstring("Creates an instance of an interface by GUID value of name.")]
        HRESULT CreateInterface(    [in] LPGUID pguidInterface,
                                    [in, defaultvalue(NULL)] BSTR bstrName,
                                    [in, defaultvalue(NULL)] LONG *pUserData,
                                    [out] LPUNKNOWN *ppInterface);
    };
cpp_quote("#endif // !_LPSCARDMANGEMENT_DEFINED")

cpp_quote("")
cpp_quote("//////////////////////////////////////////////////////////////////")
cpp_quote("//")
cpp_quote("// ISCardVerify Interface")
cpp_quote("//")

cpp_quote("#ifndef _LPSCARDVERIFY_DEFINED")
cpp_quote("#define _LPSCARDVERIFY_DEFINED")
    [
        object,
        uuid(4029DD85-5902-11D0-B84C-00C04FD424B9),
        dual,
        helpstring("ISCardVerify Interface"),
        pointer_default(unique)
    ]
    interface ISCardVerify : IDispatch
    {
        // Includes -

        import "oaidl.idl";

        // Pointer(s) -

        typedef [unique] ISCardVerify* LPSCARDVER;
        typedef [unique] LPSCARDVER LPSCARDVERIFY;

        // Properties -

        // Methods -
        [id(0),
         helpstring("Change the current user code")]
        HRESULT ChangeCode( [in]    LPBYTEBUFFER pOldCode,
[in]    LPBYTEBUFFER pNewCode,
[in]    SCARD_FLAGS Flags,
[in]    LONG lRef);

        [id(1),
         helpstring("Called to initialize the object. This is an internal ONLY use function."),
         hidden]
        HRESULT Initialize( LONG* lp );

        [id(2),
         helpstring("Unblock a blocked IHV")]
        HRESULT Unblock([in]    LONG lData,
[in]    SCARD_FLAGS Flags,
[in]    LONG lRef);

        [id(3),
         helpstring("Resets the security context.")]
        HRESULT ResetSecurityState( [in]SCARD_FLAGS Flags);

        [id(4),
         helpstring("Authenticate the user.")]
        HRESULT Verify([in]LPBYTEBUFFER pCode,
[in]SCARD_FLAGS Flags,
[in,out]LONG* lpRef);
    };
cpp_quote("#endif // !_LPSCARDVERIFICATION_DEFINED")

cpp_quote("")
cpp_quote("//////////////////////////////////////////////////////////////////")
cpp_quote("//")
cpp_quote("// SCardCom Type Library")
cpp_quote("//")

[
    uuid(4029DD83-5902-11D0-B84C-00C04FD424B9),
    version(1.0),
    helpstring("SCardCom 1.0 Type Library")
]
library SCARDCOMLib
{
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");

    [
        uuid(B0598981-6E6F-11d0-B859-00C04FD424B9),
        helpstring("SCardAuth Class")
    ]
    coclass SCardAuth
    {
        [default] interface ISCardAuth;
    };
    [
        uuid(4029DD8E-5902-11D0-B84C-00C04FD424B9),
        helpstring("SCardFileAccess Class")
    ]
    coclass SCardFileAccess
    {
        [default] interface ISCardFileAccess;
    };
    [
        uuid(5E586218-5A09-11d0-B84C-00C04FD424B9),
        helpstring("SCardManage Class")
    ]
    coclass SCardManage
    {
        [default] interface ISCardManage;
    };
    [
        uuid(4029DD89-5902-11D0-B84C-00C04FD424B9),
        helpstring("SCardVerify Class")
    ]
    coclass SCardVerify
    {
        [default] interface ISCardVerify;
    };
};


SCARDDEF.H

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    SCardDef

Abstract:

This file contains useful defined constants for use with smart card
vendor wrapper and low-level COM objects/interfaces

Author:

    Chris Dudley 1/8/1997

Environment:

    Win32, C++ w/Exceptions, ATL, COM/OLE

Revision History:

--*/

#ifndef __SCARDDEF_H__
#define __SCARDDEF_H__

////////////////////////////////////////////////////////////////////////////////////
//
// Following used with various ISO7816-4 ICC commands
//

// ISO7816 Select file parameter P1
#define SC_ID_MF_DF_EF0x00
#define SC_ID_CHILD_DF0x01
#define SC_ID_EF_UNDER_DF0x02
#define SC_ID_PARENT_DF0x03

#define SC_NAME_DF0x04

#define SC_PATH_FROM_MF0x08
#define SC_PATH_CURRENT_DF0x09

// ISO7816 Select File parameter P2
#define SC_ONLY_OCCUR0x00
#define SC_LAST_OCCUR0x01
#define SC_NEXT_OCCUR0x02
#define SC_PREV_OCCUR0x03

#define SC_RETURN_FCI0x00
#define SC_RETURN_FCP0x04
#define SC_RETURN_FMD0x08

// ISO7816 Verify
#define SC_GLOBAL_REF0x00
#define SC_SPECIFIC_REF0x80

// MAX number of open files
#define MAX_OPEN_FILES0x0A
// maximum handle value
#define MAX_HANDLE_VALUE0xFFFFFFFF

// Vendor Specific Info
#define VENDOR_CLASS_ID0xC0

// lengths
#define SC_FILE_ID_LENGTH2*sizeof(BYTE)
#define SC_FILE_ID_STR_LENGTH4
#define BYTE_BUFFER_HEAD0

// Status
#define SC_STATUS_SUCCESS0x9000
#define SC_STATUS_MOREDATA0x61

///////////////////////////////////////////////////////////////////////////////////

#endif   // __SCARDDEF_H__


SCARDFIL.CPP

//////////////////////////////////////////////////////////////////////////
//
// Includes
//

#include "stdafx.h"
#include "SCardCOM.h"
#include "SCardFil.h"

#include "scarddef.h"

///////////////////////////////////////////////////////////////////////////
// Macros
//
#ifdef _DEBUG
#define TRACE_STR(name,sz) \
TRACE(_T("SCardCOM.DLL: CSCardFileAccess::%s: %s\n"), name, sz)
#define TRACE_CODE(name,code) \
TRACE(_T("SCardCOM.DLL: CSCardFileAccess::%s: error = 0x%x\n"), name, code)
#define TRACE_CATCH(name,code)TRACE_CODE(name,code)
#define TRACE_CATCH_UNKNOWN(name)TRACE_STR(name,_T("An unidentified exception has occurred!"))
#else
#define TRACE_STR(name,sz)((void)0)
#define TRACE_CODE(name,code)((void)0)
#define TRACE_CATCH(name,code)((void)0)
#define TRACE_CATCH_UNKNOWN(name)((void)0)
#endif  // _DEBUG

/*++

CSCardFileAccess::FinalConstruct:

    This routine defines a final constructor that is called after the constructor
for the template class is called.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

HRESULT CSCardFileAccess::FinalConstruct()
{
// Locals.
HRESULT hresult = S_OK;

try {
m_iOpenFiles = 0;
m_FileInfoMap.InitHashTable(13);
m_hfileNextHandle = 1;
m_wCurrentFileID = 0x3f00;
m_Manage = NULL;
}

catch (...) {
hresult = E_UNEXPECTED;
TRACE_CATCH(_T("FinalConstruct"), hresult);
}

return (hresult);
}

/*++

CSCardFileAccess::FinalRelease:

    This routine defines a final release.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

HRESULT CSCardFileAccess::FinalRelease()
{
// Locals.
HRESULT hresult = S_OK;

try {
// Decrement the reference count on the "creation" object
if (m_Manage != NULL)
m_Manage->Release();
}

catch (...) {
}

return (hresult);
}

/*++

CSCardFileAccess::ConvertFilename

    This routine converts a filename (BSTR) to a word for internal use
as the file ID. BSTR MUST be in hex!

Arguments:

    bstrFilename - filename as BSTR

Return Value:

A word containing the filename or 0 if failed.

Author:

Note:

--*/

WORD CSCardFileAccess::ConvertFilename( BSTR bstrFilename)
{
//locals
WORDwFileID = 0;
intnStringLength = :: SysStringLen(bstrFilename);
LPTSTRpszBuffer,
pszEndBuffer;
HLOCALhMem;
DWORDdwErr;
#ifdef _UNICODE
intnNewStrLen = sizeof(WCHAR);
#else
int nNewStrLen = sizeof(char);
#endif

m_CriticalSection.EnterCriticalSection();

try {
if ( (nStringLength > SC_FILE_ID_STR_LENGTH) ||
 (nStringLength <=0) )
throw ( (HRESULT) E_INVALIDARG );
// Alloc mem for BSTR to LPTSTR conversion if required
nNewStrLen *= nStringLength;
nNewStrLen++;  //For NULL termination
#ifdef _UNICODE
pszBuffer = (LPTSTR) bstrFileName;
#else
hMem = ::LocalAlloc(LPTR, (UINT) nNewStrLen);
if (hMem == NULL) {
dwErr = ::GetLastError();
throw (HRESULT_FROM_WIN32(dwErr));
};
pszBuffer = (LPTSTR) ::LocalLock(hMem);
if (pszBuffer == NULL) {
dwErr = ::GetLastError();
throw (HRESULT_FROM_WIN32(dwErr));
};
// Convert wide char to ansi string
WideCharToMultiByte(CP_ACP,
0,
bstrFilename,
nStringLength,
pszBuffer,
nNewStrLen,
NULL,
NULL );
#endif
// Now convert to WORD...
pszEndBuffer = pszBuffer + nStringLength;
// Use correct conversion per UNICODE
#ifdef _UNICODE
wFileID = (WORD) wcstol( pszBuffer , &pszEndBuffer, 16);
#else
wFileID = (WORD) strtol( pszBuffer , &pszEndBuffer, 16);
#endif
}

catch (...) {
wFileID = 0;
TRACE_CATCH(_T("ConvertFilename"), wFileID);
}

// Eliminate the temp buffer
while (::LocalUnlock(hMem));
// At this point, ::GetLastError() should equal NO_ERROR!
if (::LocalFree(hMem) != NULL) {
wFileID = 0;
}

m_CriticalSection.ExitCriticalSection();

return wFileID;
}

/*++

CSCardFileAccess::CreateFileHandle

    This routine creates a file handle.

Arguments:

    None

Return Value:

A HSCARD_FILE handle or 0 if call failed.

Author:

--*/
HSCARD_FILE CSCardFileAccess::CreateFileHandle()
{
// locals
HSCARD_FILE hFile = 0;

m_CriticalSection.EnterCriticalSection();

try {
// Simple generation based on successive HSCARD_FILE values.
if (m_hfileNextHandle == MAX_HANDLE_VALUE)
throw(hFile = 0);
hFile = m_hfileNextHandle;
m_hfileNextHandle++;
}

catch (HSCARD_FILE handle) {
hFile = handle;
TRACE_CATCH(_T("CreateFileHandle"), hFile);
}

catch (...) {
hFile = 0;
TRACE_CATCH_UNKNOWN(_T("CreateFileHandle"));
}

m_CriticalSection.ExitCriticalSection();

return hFile;
}

/*++

CSCardFileAccess::FileSelect

    This routine selects file/changes directory on the ICC.

Arguments:

    sFilePath - 2-byte file ID of file to be selected

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

Notes:

FCI is requested but NOT stored within this object. To store
the FCI data, create a IByteBuffer (aka IStream) and call
ISCard->get_ReplyApdu(..).

--*/

HRESULT CSCardFileAccess::FileSelect( WORD wFilePath )
{
HRESULT hresult = S_OK;
LPBYTEBUFFERpData = NULL,
pResultData = NULL;
ULONGulLength = 1L,
ulBytesWritten = 0;
BYTEbyID = 0,
byP1 = 0,
byP2 = 0;
LPSCARDCMDpCmd = NULL;

m_CriticalSection.EnterCriticalSection();

try {
// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

if (ulLength>MAX_PATH_LEN)
throw ( (HRESULT) E_INVALIDARG );

// Create Cmd Object
hresult = m_Manage->CreateCmdObject(&pCmd);
if (FAILED(hresult))
throw (hresult);

// Create Data grabber
CGrabDataGrabData(pCmd,
m_Manage->m_pISCard,
m_Manage->m_pISCardISO7816,
m_Manage->m_pISCardTypeConv);

// Create a byte buffer for ISO call
hresult = m_Manage->m_pISCardTypeConv->CreateByteBuffer(ulLength,
&pData);
if ( FAILED(hresult) )
throw (hresult);

// Write data to the buffer - High byte of file ID first!
byID = HIBYTE(wFilePath);
pData->Write( (BYTE*) &byID, (LONG) ulLength, (LONG*) &ulBytesWritten );
byID = LOBYTE(wFilePath);
pData->Write( (BYTE*) &byID, (LONG) ulLength, (LONG*) &ulBytesWritten );

// Create the p1-p2 paramters
byP1 = SC_ID_MF_DF_EF;
byP2 = (SC_ONLY_OCCUR | SC_RETURN_FCI);
// ISO7186 COM builds command
hresult = m_Manage->m_pISCardISO7816->SelectFile(byP1,
byP2,
pData,
0,
&pCmd);

if (FAILED(hresult))
throw hresult;

// Force correct class id for the vendor's card
hresult = pCmd->put_ClassId(VENDOR_CLASS_ID);
if (FAILED(hresult))
throw (hresult);

hresult = m_Manage->m_pISCard->LockSCard();
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->Transaction(&pCmd);
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->UnlockSCard(LEAVE);
if (FAILED(hresult))
throw (hresult);

//interpret return APDU
hresult = pCmd->get_ApduReplyLength(&m_lReplyLength);
if (FAILED(hresult))
throw (hresult);
hresult = pCmd->get_ReplyStatus( &m_wReplyStatus );
if (FAILED(hresult))
throw (hresult);
if (HIBYTE(m_wReplyStatus)==SC_STATUS_MOREDATA) {
hresult = GrabData.GetExtraData(&pResultData,
&m_wReplyStatus,
&m_lReplyLength);
if (FAILED(hresult))
throw (hresult);
// Todo: Store returned file specfic data??
}
else if (m_wReplyStatus!=SC_STATUS_SUCCESS)
throw ( (HRESULT) E_FAIL );
}

catch (HRESULT hr) {
hresult = hr;
TRACE_CATCH(_T("FileSelect"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("FileSelect"));
}

// Free the ByteBuffer
if (pData != NULL)
pData->Release();
if (pResultData != NULL)
pResultData->Release();
// Release cmd interface
if (pCmd != NULL)
pCmd->Release();

m_CriticalSection.ExitCriticalSection();

return hresult;
}

/////////////////////////////////////////////////////////////////////////////
//
// ISCardFileAccess Methods
//

/*++

CSCardFileAccess::ChangeDir:

This method changing the current smartcard directory to the new
specified directory.

Arguments:

    refType - specifies the type of reference used in PathSpec as follows
SC_TYPE_BY_NAME
SC_TYPE_BY_ID
SC_TYPE_BY_SHORT_ID
SC_TYPE_BY_ANY
  in this example code we are only supporting SC_TYPE_BY_ID

NewDir - Specifies an File to select. (It must be < 4 for this example.)

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

Notes:

This example code and assumes a ISO7816 supported file structure on
the ICC and passes the given bstrNewDir directly too the corresponding ISO7816
command as an absolute path only (i.e. beginning from MF).

--*/

STDMETHODIMP CSCardFileAccess::ChangeDir(
REFTYPE refType,
BSTR bstrNewDir)
{
//locals
HRESULThresult = S_OK;
WORDwFileID;

try {
// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

// only supporting SC_TYPE_BY_ID
if ( refType != SC_TYPE_BY_ID )
throw ( (HRESULT) E_INVALIDARG );
// convert and store
wFileID = ConvertFilename(bstrNewDir);
hresult = FileSelect(wFileID);
if (FAILED(hresult))
throw (hresult);
// success...store the path
m_wCurrentFileID = wFileID;
}

catch (HRESULT hr) {
hresult = hr;
TRACE_CATCH(_T("ChangeDir"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("ChangeDir"));
}

return hresult;
}

/*++

CSCardFileAccess::Close:

This method closes the specified file. No further access to file is
allowed.

Arguments:

    hFile - An HSCARD_FILE to the file to be closed.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

Notes:

Use of Open or Close methods involves updating an internal CMap object which
tracks the current open file information (handle, name, etc).

--*/

STDMETHODIMP CSCardFileAccess::Close(
HSCARD_FILE hFile)
{
//locals
HRESULThresult = S_OK;
FILE_INFORMATIONFileInfo;

try {
// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

m_CriticalSection.EnterCriticalSection();
// Lookup the handle in the file map
if ( !m_FileInfoMap.Lookup(hFile, FileInfo) )
throw ( (HRESULT) E_HANDLE);

if ( !m_FileInfoMap.RemoveKey(hFile) )
throw ( (HRESULT) E_FAIL);
m_CriticalSection.ExitCriticalSection();
}

catch (HRESULT hr) {
hresult = hr;
TRACE_CATCH(_T("Close"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("Close"));
}

return hresult;
}

/*++

CSCardFileAccess::Create:

The method creates a file at a given location within the smartcard file
system.

Arguments:

    refType - Specifies the type of file to be created (directory, fixed length,
etc.)

bstrPathSpec - specifies file to be created within current context

ppTLVs - list of TLV structures, with file properties that have to be set.

lpcTLVs -- number of entries in TLV

Flags - specifies whether Secure Messaging should be used and data
pre-allocated

pDataBuffer - pointer to pre-allocated data

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardFileAccess::Create(
REFTYPE refType,
BSTR bstrPathSpec,
LPTLV_TABLE* ppTLVs,
LONG* lpcTLVs,
SCARD_FLAGS Flags,
LPBYTEBUFFER pDataBuffer)
{
//locals
HRESULThresult = E_NOTIMPL;

// to do
// This method should be implemented if the ICC supports this command.

return hresult;
}

/*++

CSCardFileAccess::Delete:

The method deletes a file at a given location.

Arguments:

refType - references filename type (ID, short ID, etc) used in
bstrPathSpec.

    bstrPathSpec - specifies file to be deleted

Flags - specifies whether secure messaging should be used. In the case of a
directory file(DF), this parameter specifies whether complete tree below the
dir should be deleted

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

Note:

This is an EXAMPLE of using ISO7816 Erase Binary to remove a file. This should
be updated to support your specific ICC and file type(s).

--*/

STDMETHODIMP CSCardFileAccess::Delete(
REFTYPE refType,
BSTR bstrPathSpec,
SCARD_FLAGS Flag)
{
//locals
HRESULThresult = E_NOTIMPL;

try {
// Todo:
}

catch (HRESULT hr) {
hresult = hr;
TRACE_CATCH(_T("Delete"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("Delete"));
}

return hresult;
}

/*++

CSCardFileAccess::Directory:

Retrieve a list of files of the specified type.

Arguments:

    fileType - specifies the type of files to be listed

ppFileList - returned list of specified files as array of BSTRs

Part 6 of the PC/SC Interoperability Specification for ICCs and Personal
Computer Systems calls for a variable describing the length of ppFileList;
as BSTRs include their length, this variable is excluded from this
example implementation.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardFileAccess::Directory(
FILETYPE fileType,
LPSAFEARRAY *ppFileList)
{
//locals
HRESULThresult = E_NOTIMPL;

// to do
// This method should be implemented if the ICC supports this command.

return hresult;
}

/*++

CSCardFileAccess::GetCurrentDirectory:

This method returns an absolute path to the currently selected directory

Arguments:

    pbstrPathSpec - pointer to a BSTR containing the path

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

Notes:

1) The calling application MUST free this global memory allocated for
the BSTR.

--*/

STDMETHODIMP CSCardFileAccess::GetCurrentDir(
BSTR* pbstrPathSpec)
{
//locals
HRESULThresult = E_NOTIMPL;

// Todo:
// Add return directory code.

return hresult;
}

/*++

CSCardFileAccess::GetFileCapabilities

Retrieve list of file capabilities.

Arguments:

    ppProperties - list of TLV_TABLE structures indicating the files for which
to get properties on input, and containing those properties on output.

plProperties - pointer to LONG containing the number of elements in
pProperties

Flags - flags

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardFileAccess::GetFileCapabilities(
LPTLV_TABLE* ppProperties,
LONG *plProperties,
SCARD_FLAGS Flags)
{
//locals
HRESULThresult = E_NOTIMPL;

// to do
return hresult;
}

/*++

CSCardFileAccess::GetProperties

Retrieve the primitive data refered by tags (TLV) for the specified
object

Arguments:

    refType - specifies the type of reference to the file

bstrPathSpec - specifies the file to use.

ppTLV - point to TLV structs whose value has been retrieved.

plcTLV - number of entries in ppTLV

Flags - specifies whether Secure Messaging has to be used.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardFileAccess::GetProperties(
REFTYPE refType,
BSTR bstrPathSpec,
LPTLV_TABLE* ppTLV,
LONG* plcTLV,
SCARD_FLAGS Flags)
{
//locals
HRESULThresult = E_NOTIMPL;

// to do
return hresult;
}

/*++

CSCardAuth::Initialize

Initializes the object for use.

Arguments:

lp - a long pointer to the "controlling" ISCardManage object.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardFileAccess::Initialize( LONG *lp)
{
// Locals
HRESULThresult = S_OK;

try {
// Check Params, etc..
if (lp == NULL)
throw ( (HRESULT) E_INVALIDARG );

if (m_Manage != NULL)
throw ( (HRESULT) E_FAIL );

// Ok...
m_Manage = (LPCSCARDMANAGE) lp;
}

catch (HRESULT hr) {
hresult=hr;
TRACE_CATCH(_T("Initialize"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("Initialize"));
}

return (hresult);
}

/*++

CSCardFileAccess::Invalidate:

Invalidate the specified file (EF or DF). An invalidated file cannot
be accessed by other methods prior to using rehabilitate

Arguments:

    bstrPathSpec - specifies file to be invalidated (relative path)

Flag - specifies whether Secure Messaging is to be used

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardFileAccess::Invalidate(
BSTR bstrPathSpec,
SCARD_FLAGS Flags)
{
//locals
HRESULThresult = E_NOTIMPL;

// to do
// This method should be implemented if the ICC supports this command.

return hresult;
}

/*++

CSCardFileAccess::Open

Opens the specified file for further use

Arguments:

    refType - Specifies the type of reference to the file

bstrPathSpec - specifies the file to open.

hFile - HSCARD_FILE to file if successful, NULL otherwise.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

Notes:

This method DOES NOT read file headers. It defaults to setting the
class type as transparent EF.

--*/

STDMETHODIMP CSCardFileAccess::Open(
REFTYPE refType,
BSTR bstrPathSpec,
HSCARD_FILE *phFile)
{
//locals
HRESULThresult = E_NOTIMPL;
FILE_INFORMATIONFileInfo;
WORDwFileID;

try {
// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

if ( (bstrPathSpec == NULL) || (phFile == NULL) )
throw ( (HRESULT) E_INVALIDARG );
// Does file exist?
wFileID = ConvertFilename(bstrPathSpec);
if (wFileID == 0 )
throw ( (HRESULT) E_FAIL );

hresult = FileSelect( wFileID );
if (FAILED(hresult))
throw (hresult);

*phFile = CreateFileHandle();
if (*phFile == 0)
throw ( (HRESULT) E_FAIL);

m_CriticalSection.EnterCriticalSection();
// Create file info struct and add to map
FileInfo.fileHandle = *phFile;
FileInfo.fileStatus= SC_FL_STAT_DEFAULT;
FileInfo.wfileName = wFileID;
// Assume transparent EF for this example.
FileInfo.fileType = SC_TYPE_TRANSPARENT_EF;
FileInfo.lSeekOffset = 0;
m_FileInfoMap.SetAt(*phFile,FileInfo);
m_CriticalSection.ExitCriticalSection();

}

catch (HRESULT hr) {
hresult = hr;
TRACE_CATCH(_T("Open"),hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("Open"));
}

return hresult;
}

/*++

CSCardFileAccess::Read

This method reads and returns the specifed data from a given file

Arguments:

    HSCARD_FILE - contains the handle of the file to access

lBytesToRead - length of the data object to be read from file

ppBuffer - if successful, contains the read data

Flags - specifies whether Secure Messaging is to be used

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

Notes:

As is this routine mainly supports the write to a transparent EF. This
is the default given to the file type on Open within this class.

--*/

STDMETHODIMP CSCardFileAccess::Read(
HSCARD_FILE hFile,
LONG *lBytesToRead,
LPBYTEBUFFER *ppBuffer,
SCARD_FLAGS Flags)
{
//locals
HRESULThresult = S_OK;
FILE_INFORMATIONFileInfo;
LPBYTEBUFFERpData = NULL;
LPSCARDCMDpCmd = NULL;
LONGcbBufferSize = 0,
cbToRead = 0;
BYTErgbyReadData[MAX_READ_LEN],
byP1 = 0,
byP2 = 0;
WORDwOffset = 0;
LONGlReplyLength = 0;
LARGE_INTEGERli;

try {
// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

// Check Params...
if (*lBytesToRead == NULL)
throw ( (HRESULT) E_POINTER);
if (*lBytesToRead <=0 )
throw ( (HRESULT) E_INVALIDARG );

// Create return buffer if required
if (*ppBuffer == NULL) {
// Create z new buffer
hresult = m_Manage->m_pISCardTypeConv->CreateByteBuffer((ULONG) *lBytesToRead,
ppBuffer);
if ( FAILED(hresult) )
throw (hresult);
};

m_CriticalSection.EnterCriticalSection();
// Check for valid handle
if (!m_FileInfoMap.Lookup(hFile, FileInfo))
throw ( (HRESULT) E_HANDLE );
m_CriticalSection.ExitCriticalSection();
// Change directory
hresult = FileSelect(FileInfo.wfileName);
if (FAILED(hresult))
throw (hresult);

// Create Cmd Object
hresult = m_Manage->CreateCmdObject(&pCmd);
if (FAILED(hresult))
throw (hresult);

// Set the size for amount of data to be read from file
cbBufferSize = (*lBytesToRead);

// set Seek position to the head of buffer where return data will be read
LISet32 (li, BYTE_BUFFER_HEAD );
hresult = (*ppBuffer)->Seek((LONG) li.LowPart,
(LONG) STREAM_SEEK_SET,
NULL);
if (FAILED(hresult))
throw hresult;

// Loop to read complete file into retur buffer
while (cbBufferSize > 0) {
// Determine amount of data to write
if (cbBufferSize <= MAX_READ_LEN) {
// Set bytes to read
cbToRead = cbBufferSize;
// Last time through, clear it
cbBufferSize = 0;
}
else {
// Read max bytes
cbToRead = MAX_READ_LEN;
// Decrement total number bytes remaining to read
cbBufferSize -= MAX_READ_LEN;
};

// Build correct command based on file type
switch (FileInfo.fileType) {
case SC_TYPE_TRANSPARENT_EF:
byP1 = HIBYTE(wOffset);
byP2 = LOBYTE(wOffset);
// Build Update command for TRANSPARENT (i.e. Binary) file
hresult = m_Manage->m_pISCardISO7816->ReadBinary(   byP1,
byP2,
cbToRead,
&pCmd);
if (FAILED(hresult))
throw (hresult);
// Calculate the new offset for the command

wOffset += (WORD) cbToRead;
break;
default:
break;
};// switch

// Force correct class id for the vendor's card
hresult = pCmd->put_ClassId(VENDOR_CLASS_ID);
if (FAILED(hresult))
throw (hresult);

// Do complete transaction
hresult = m_Manage->m_pISCard->LockSCard();
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->Transaction(&pCmd);
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->UnlockSCard(LEAVE);
if (FAILED(hresult))
throw (hresult);

// Get status of command
hresult = pCmd->get_ReplyStatus( &m_wReplyStatus );
if (FAILED(hresult))
throw (hresult);
if (m_wReplyStatus != SC_STATUS_SUCCESS)
throw ( (HRESULT) E_FAIL );

// Get reply data (i.e. the data that was read from file) and length
hresult = pCmd->get_ApduReply( &pData );
if (FAILED(hresult))
throw (hresult);
hresult = pCmd->get_ApduReplyLength(&lReplyLength);
if (FAILED(hresult))
throw (hresult);
if (lReplyLength<0)
throw ((HRESULT) E_FAIL);

// Set Seek pointer of return buffer
hresult = pData->Seek((LONG) li.LowPart,
(LONG) STREAM_SEEK_SET,
NULL);
if (FAILED(hresult))
throw hresult;
// Read reply data into temp buffer
hresult = pData->Read((LPBYTE) rgbyReadData,
(LONG) (lReplyLength-2), //Read reply data-status bytes
NULL);
if (FAILED(hresult))
throw (hresult);
// Write temp buffer back into return buffer
hresult = (*ppBuffer)->Write((LPBYTE) rgbyReadData,
(LONG) (lReplyLength-2), //Read reply data-status bytes
NULL);
if (FAILED(hresult))
throw (hresult);
// Release the Reply buffer
if (pData != NULL)
pData->Release();
}; //while - write buffer
}

catch (HRESULT hr) {
hresult = hr;
TRACE_CATCH(_T("Read"),hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("Read"));
}

// Clean Up
if (pCmd != NULL)
pCmd->Release();

return hresult;
}

/*++

CSCardFileAccess::Rehabilitate

Makes a file (EF or DF), which has been previously invalidated with the
invalidate command, accessable by the application.

Arguments:

    bstrPathSpec - specifies the file to be invalidated (relative path)

Flag - specifies whether Secure Messaging is to be used

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardFileAccess::Rehabilitate(
BSTR bstrPathSpec,
SCARD_FLAGS Flags)
{
//locals
HRESULThresult = E_NOTIMPL;

// to do
// This method should be implemented if the ICC supports this command.

return hresult;
}

/*++

CSCardFileAccess::Seek

Selects the object form which (read/write) access will be done

Arguments:

    hFile - contains the handle of the file to access

lOffset - is the number of data objects from the reference object

Seek - specifies the type of seek access on the file

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

Notes:

--*/

STDMETHODIMP CSCardFileAccess::Seek(
HSCARD_FILE hFile,
LONG lOffset,
SEEKTYPE Seek)
{
//locals
HRESULThresult = S_OK;
FILE_INFORMATIONFileInfo;

try {
// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

// Check Params...
if (lOffset <0)
throw ( (HRESULT) E_INVALIDARG);
// We are only seeking from beginning of the file
if (Seek != SC_SEEK_FROM_BEGINNING)
throw ( (HRESULT) E_INVALIDARG );

m_CriticalSection.EnterCriticalSection();
// Check for valid handle
if (!m_FileInfoMap.Lookup(hFile, FileInfo))
throw ( (HRESULT) E_HANDLE );
// Store the seek/record position
FileInfo.lSeekOffset = lOffset;
m_FileInfoMap.SetAt(hFile, FileInfo);
m_CriticalSection.ExitCriticalSection();

}

catch (HRESULT hr) {
hresult = hr;
TRACE_CATCH(_T("Seek"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("Seek"));
}

return hresult;
}

/*++

CSCardFileAccess::SetProperties

Set the primitive data refered by tags (TLV) for the specified
object

Arguments:

    refType - specifies the type of reference to the file

bstrPathSpec - specifies the file to use (relative or absolute path).

pTLV - point to TLV structs whose values have to set

lcTLV - number of entries in ppTLV

Flags - specifies whether Secure Messaging has to be used.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardFileAccess::SetProperties(
REFTYPE refType,
BSTR bstrPathSpec,
LPTLV_TABLE pTLV,
LONG lcTLV,
SCARD_FLAGS Flags)
{
//locals
HRESULThresult = E_NOTIMPL;

// to do
return hresult;
}

/*++

CSCardFileAccess::Write

This method writes data to the specified file.

Arguments:

    HSCARD_FILE - contains the handle of the file to access

lLength - length of the data object to write

pData - contains the object/data to be written

Flags - specifies whether Secure Messaging is to be used

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

Revision History

Note:

As is this routine mainly supports the write to a transparent EF. This
is the default given to the file type on Open within this class.

--*/

STDMETHODIMP CSCardFileAccess::Write(
HSCARD_FILE hFile,
LONG lLength,
LPBYTEBUFFER pData,
SCARD_FLAGS Flags)
{
//locals
HRESULThresult = S_OK;
FILE_INFORMATIONFileInfo;
LPBYTEBUFFERpBuffer = NULL;
LPSCARDCMDpCmd = NULL;
LONGcbBufferSize = 0,
cbToWrite = 0;
BYTErgbyWriteData[MAX_WRITE_LEN],
byP1 = 0,
byP2 = 0;
STATSTRUCTstatstruct;
WORDwOffset = 0;
LARGE_INTEGERli;

try {
// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

// Check Params...
if (pData == NULL)
throw ( (HRESULT) E_POINTER);

m_CriticalSection.EnterCriticalSection();
// Check for valid handle
if (!m_FileInfoMap.Lookup(hFile, FileInfo))
throw ( (HRESULT) E_HANDLE );
m_CriticalSection.ExitCriticalSection();
// Change directory
hresult = FileSelect(FileInfo.wfileName);
if (FAILED(hresult))
throw (hresult);

// Create Cmd Object
hresult = m_Manage->CreateCmdObject(&pCmd);
if (FAILED(hresult))
throw (hresult);

// Get the write buffer statistics
hresult = pData->Stat(&statstruct, 0L );
if (FAILED(hresult))
throw (hresult);
if (statstruct.cbSize <= 0)
throw ( (HRESULT) E_FAIL );
cbBufferSize = statstruct.cbSize;

// set Seek position/Read challenge to buffer's head
LISet32 (li, BYTE_BUFFER_HEAD );
hresult = pData->Seek((LONG) li.LowPart,
(LONG) STREAM_SEEK_SET,
NULL);
if (FAILED(hresult))
throw hresult;

// Create new buffer for use in holding portion of data to be written to ICC
hresult = m_Manage->m_pISCardTypeConv->CreateByteBuffer(1L,
&pBuffer);
if ( FAILED(hresult) )
throw (hresult);

// Loop to write complete buffer
while (cbBufferSize > 0) {
// Determine amount of data to write
if (cbBufferSize <= MAX_WRITE_LEN) {
// Set bytes to write
cbToWrite = cbBufferSize;
// Last time through, clear it
cbBufferSize = 0;
}
else {
// Write max bytes
cbToWrite = MAX_WRITE_LEN;
// Decrement total number bytes remaining to write
cbBufferSize -= MAX_WRITE_LEN;
};

// Read information from parameter write buffer
hresult = pData->Read((LPBYTE) rgbyWriteData,
cbToWrite,
NULL);
if (FAILED(hresult))
throw (hresult);

// Set Seek pointer to write into temp buffer at its head
hresult = pBuffer->Seek((LONG) li.LowPart,
(LONG) STREAM_SEEK_SET,
NULL);
if (FAILED(hresult))
throw hresult;

// Set buffer size and write information
hresult = pBuffer->SetSize( cbToWrite );
hresult = pBuffer->Write((LPBYTE) rgbyWriteData,
cbToWrite,
NULL);
if (FAILED(hresult))
throw (hresult);

// Build correct command based on file type
switch (FileInfo.fileType) {
case SC_TYPE_TRANSPARENT_EF:
byP1 = HIBYTE(wOffset);
byP2 = LOBYTE(wOffset);
// Build Update command for TRANSPARENT (i.e. Binary) file
hresult = m_Manage->m_pISCardISO7816->UpdateBinary( byP1,
byP2,
pBuffer,
&pCmd);
if (FAILED(hresult))
throw (hresult);
// Calculate the new offset for the command
wOffset += (WORD) cbToWrite;
break;
default:
break;
};// switch

// Force correct class id for the vendor's card
hresult = pCmd->put_ClassId(VENDOR_CLASS_ID);
if (FAILED(hresult))
throw (hresult);

// Do complete transaction
hresult = m_Manage->m_pISCard->LockSCard();
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->Transaction(&pCmd);
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->UnlockSCard(LEAVE);
if (FAILED(hresult))
throw (hresult);

//interpret return APDU
hresult = pCmd->get_ReplyStatus( &m_wReplyStatus );
if (FAILED(hresult))
throw (hresult);
if (m_wReplyStatus != SC_STATUS_SUCCESS)
throw ( (HRESULT) E_FAIL );
}; //while - write buffer
}

catch (HRESULT hr) {
hresult = hr;
TRACE_CATCH(_T("Write"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("Write"));
}

// Clean UP
if (pBuffer != NULL)
pBuffer->Release();

if (pCmd != NULL)
pCmd->Release();

return hresult;
}


SCARDFIL.H

// SCardFil.h : Declaration of the CSCardFileAccess

#ifndef __SCARDFILEACCESS_H_
#define __SCARDFILEACCESS_H_

#include "resource.h"       // main symbols
#include "scardman.h"

/////////////////////////////////////////////////////////////////////////////
// CSCardFileAccess
class ATL_NO_VTABLE CSCardFileAccess :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CSCardFileAccess, &CLSID_SCardFileAccess>,
public IDispatchImpl<ISCardFileAccess, &IID_ISCardFileAccess, &LIBID_SCARDCOMLib>
{
// Members
private:
LPCSCARDMANAGEm_Manage;
// Struct and map used for holding file information for open files, etc.
typedef struct tagfile_info {
HSCARD_FILEfileHandle;
SCARD_FLAGSfileStatus;
FILETYPEfileType;
WORDwfileName;
LONGlSeekOffset;
LPCSTRszRFU;
} FILE_INFORMATION;
CMap<HSCARD_FILE, HSCARD_FILE, FILE_INFORMATION, FILE_INFORMATION&> m_FileInfoMap;

intm_iOpenFiles;
HSCARD_FILEm_hfileNextHandle;
WORDm_wCurrentFileID;
LONGm_lReplyLength;
WORDm_wReplyStatus;

CCriticalSectionm_CriticalSection;

public:
CSCardFileAccess(){}

~CSCardFileAccess(){}

HRESULT FinalConstruct();

HRESULT FinalRelease();

//
//DECLARE_NOT_AGGREGATABLE(CSCardFileAccess)
// Note:
// Remove the comment from the line above if you don't want your object to
// support aggregation.  The default is to support it
//

//Registry:

//Simple ALL 1.0 based registry entry
DECLARE_REGISTRY(CSCardFileAccess,
_T("SCardCOM.SCardFileAccess.1"),
_T("SCardCOM.SCardFileAccess"),
IDS_SCARDFIL_DESC,
THREADFLAGS_APARTMENT)
// Note: Enters the standard class registration into the system
// registry.
//
// Script based...
// DECLARE_REGISTRY_RESOURCEID(IDR_SCARDFILEACCESS)

BEGIN_COM_MAP(CSCardFileAccess)
COM_INTERFACE_ENTRY(ISCardFileAccess)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

private:
// ISCardFileAccess Methods
WORD ConvertFilename(BSTR bstrFilename);
HSCARD_FILE CreateFileHandle();
HRESULT FileSelect(WORD wFileName);

public:
// ISCardFileAccess Methods
STDMETHOD(ChangeDir)(
REFTYPE refType,
BSTR bstrNewDir);
STDMETHOD(Close)(
HSCARD_FILE hFile);
STDMETHOD(Create)(
REFTYPE refType,
BSTR bstrPathSpec,
LPTLV_TABLE* ppTLVs,
LONG* lpcTLVs,
SCARD_FLAGS Flags,
LPBYTEBUFFER pDataBuffer);
STDMETHOD(Delete)(
REFTYPE refType,
BSTR bstrPathSpec,
SCARD_FLAGS Flag);
STDMETHOD(Directory)(
FILETYPE fileType,
LPSAFEARRAY *ppFileList);
STDMETHOD(GetCurrentDir)(
BSTR* pbstrPathSpec);
STDMETHOD(GetFileCapabilities)(
LPTLV_TABLE* ppProperties,
LONG* plProperties,
SCARD_FLAGS Flags);
STDMETHOD(GetProperties)(
REFTYPE refType,
BSTR bstrPathSpec,
LPTLV_TABLE *ppTLV,
LONG* plcTLV,
SCARD_FLAGS Flags);
STDMETHOD(Initialize)(
LONG* lp);
STDMETHOD(Invalidate)(
BSTR bstrPathSpec,
SCARD_FLAGS Flags);
STDMETHOD(Open)(
REFTYPE refType,
BSTR bstrPathSpec,
HSCARD_FILE *phFile);
STDMETHOD(Read)(
HSCARD_FILE hFile,
LONG *lBytesToRead,
LPBYTEBUFFER* ppBuffer,
SCARD_FLAGS Flags);
STDMETHOD(Rehabilitate)(
BSTR bstrPathSpec,
SCARD_FLAGS Flags);
STDMETHOD(Seek)(
HSCARD_FILE hFile,
LONG lOffset,
SEEKTYPE Seek);
STDMETHOD(SetProperties)(
REFTYPE refType,
BSTR bstrPathSpec,
LPTLV_TABLE pTLV,
LONG lcTLV,
SCARD_FLAGS Flags);
STDMETHOD(Write)(
HSCARD_FILE hFile,
LONG lLength,
LPBYTEBUFFER pData,
SCARD_FLAGS Flags);
};

#endif //__SCARDFILEACCESS_H_


SCARDMAN.CPP

// SCardMan.cpp : Implementation of CSCardManage

//////////////////////////////////////////////////////////////////////////
//
// Includes
//
#include "stdafx.h"
#include "SCardCom.h"
#include "SCardMan.h"
// Include the SSP GUIDS
#include <sspguid.h>

///////////////////////////////////////////////////////////////////////////
// Macros
//
#ifdef _DEBUG
    #define TRACE_STR(name,sz) \
                TRACE(_T("SMB_SSP.DLL: CSCardManage::%s: %s\n"), name, sz)
    #define TRACE_CODE(name,code) \
                TRACE(_T("SMB_SSP.DLL: CSCardManage::%s: error = 0x%x\n"), name, code)
    #define TRACE_CATCH(name,code)      TRACE_CODE(name,code)
    #define TRACE_CATCH_UNKNOWN(name)   TRACE_STR(name,_T("An unidentified exception has occurred!"))
#else
    #define TRACE_STR(name,sz)          ((void)0)
    #define TRACE_CODE(name,code)       ((void)0)
    #define TRACE_CATCH(name,code)      ((void)0)
    #define TRACE_CATCH_UNKNOWN(name)   ((void)0)
#endif  // _DEBUG

/////////////////////////////////////////////////////////////////////////////
// CSCardManage

/*++

FinalConstruct:

    This routine defines a final constructor that is called after the constructor
    for the template class is called.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/

HRESULT CSCardManage::FinalConstruct()
{
    // Locals.
    HRESULT hresult = S_OK;

    try {
        // Check Params, etc

        ///////////////////////////////////////////////////////////////////////////
        //
        // Create an instance of all the low-level COM objects-interfaces
        //
        //
        hresult = CoCreateInstance( CLSID_CSCard,
                                    NULL,
                                    CLSCTX_LOCAL,
                                    IID_ISCard,
                                    (LPVOID*) &m_pISCard);
        if (FAILED(hresult))
            throw (hresult);
        hresult = CoCreateInstance( CLSID_CSCardTypeConv,
                                    NULL,
                                    CLSCTX_LOCAL,
                                    IID_ISCardTypeConv,
                                    (LPVOID*) &m_pISCardTypeConv);
        if (FAILED(hresult))
            throw (hresult);
        hresult = CoCreateInstance( CLSID_CSCardISO7816,
                                    NULL,
                                    CLSCTX_LOCAL,
                                    IID_ISCardISO7816,
                                    (LPVOID*) &m_pISCardISO7816);
        if (FAILED(hresult))
            throw (hresult);
        // If Required...
        hresult = CoCreateInstance( CLSID_CSCardDatabase,
                                    NULL,
                                    CLSCTX_LOCAL,
                                    IID_ISCardDatabase,
                                    (LPVOID*) &m_pISCardDatabase);
        if (FAILED(hresult))
            throw (hresult);
        hresult = CoCreateInstance( CLSID_CSCardLocate,
                                    NULL,
                                    CLSCTX_LOCAL,
                                    IID_ISCardLocate,
                                    (LPVOID*) &m_pISCardLocate);
        if (FAILED(hresult))
            throw (hresult);
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("FinalConstruct"),hr);
    }

    catch (...) {
        hresult = E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("FinalConstruct"));
    }

    return (hresult);
}

/*++

FinalRelease:

    This routine defines a final release.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/

HRESULT CSCardManage::FinalRelease()
{
    // Locals.
    HRESULT hresult = S_OK;

    try {
        // Release the Interfaces
        m_pISCard->Release();
        m_pISCardISO7816->Release();
        m_pISCardTypeConv->Release();
        // If required...
        m_pISCardDatabase->Release();
        m_pISCardLocate->Release();
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("AttachByIFD"));
    }

    return (hresult);
}

/*++

CreateCmdObject:

    This routine creates an instance of the ISCardCmd object

Arguments:

    ppCmd - pointer to an ISCardCmd pointer

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
HRESULT CSCardManage::CreateCmdObject(LPSCARDCMD *ppCmd)
{
    // Locals
    HRESULT     hresult = S_OK;
    LPSCARDCMD  pObj = NULL;

    try {
        // Check Params, etc...
        if (ppCmd==NULL)
            throw ( (HRESULT) E_INVALIDARG );

        // Create the object
        hresult = CoCreateInstance( CLSID_CSCardCmd,
                                    NULL,
                                    CLSCTX_LOCAL,
                                    IID_ISCardCmd,
                                    (LPVOID*) &pObj);
        if (FAILED(hresult))
            throw (hresult);

        ASSERT( (pObj) != NULL );

        // Package for return
        (*ppCmd) = pObj;
    }

    catch (HRESULT hr) {
        hresult=hr;
        TRACE_CATCH(_T("CreateCmdObject"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("CreateCmdObject"));
    }

    return hresult;
}

/////////////////////////////////////////////////////////////////////////////
//
// ISCardManage Methods
//

/*++

CSCardManage::AttachByHandle:

    Allows an application to create a communication to an ICC using
    a handle returned by the Resource Manager

Arguments:

    hICC - handle to the ICC

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardManage::AttachByHandle(
                    HSCARD hICC)
{
    //locals
    HRESULT     hresult = S_OK;

    try {
        if (hICC == 0)
            throw ( (HRESULT) E_INVALIDARG);
        // Make low level COM call
        hresult = m_pISCard->AttachByHandle(hICC);
        if (FAILED(hresult))
            throw (hresult);
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("AttachByHandle"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("AttachByHandle"));
    }

    return hresult;
}

/*++

CSCardManage::AttachByIFD:

    Allows an application to request establishment of a context for
    a specific IFD referenced with a friendly name

Arguments:

    bstrIFDName - String containing friendly name

Flags - desired access mode

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardManage::AttachByIFD(
                    BSTR bstrIFDName,
SCARD_SHARE_MODES Flags)
{
    //locals
    HRESULT     hresult = S_OK;

    try {
        if (bstrIFDName == NULL)
            throw ( (HRESULT) E_INVALIDARG);
        // make low level COM call...Default to T0 protocol for Cryptoflex
        // Note: In this example code we are grabbing the reader for SHAREd
        // use. This implies we MUST use ISCard->Lock (SCardBeginTransaction) and
        // ISCard->Unlock (SCardEndTransaction) when communicating with the card.
        // The begin/end transaction functions should be called in any case.
        hresult = m_pISCard->AttachByReader(bstrIFDName, SHARED, T0);
        if (FAILED(hresult))
            throw (hresult);
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("AttachByIFD"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("AttachByIFD"));
    }

    return hresult;
}

/*++

CSCardManage::CreateCardAuth:

    Creates an instance of the ISCardAuth interface and returns
    a pointer to the created interface.

Arguments:

    pISCardAuth - pointer to ISCardAuth

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardManage::CreateCardAuth( LPSCARDAUTH* ppISCardAuth )
{
    // Locals
    HRESULT     hresult = S_OK;
    LPSCARDAUTH pObj = NULL;

    try {
        // Check params, etc..
        if (ppISCardAuth == NULL)
            throw ( (HRESULT) E_INVALIDARG );

        // Create an instance of the interface
        hresult = CoCreateInstance( CLSID_SCardAuth,
                                    NULL,
                                    CLSCTX_LOCAL,
                                    IID_ISCardAuth,
                                    (LPVOID*) &pObj);
        if (FAILED(hresult))
            throw (hresult);

        // Initialize the Interface/Object
        hresult = pObj->Initialize((LONG*) this);
        if (FAILED(hresult))
            throw (hresult);

        // Ok...
        (*ppISCardAuth) = pObj;
        // We are bumping the reference count on this object so we are sure it will
        // remain available. This means each object MUST Release() the count!
        AddRef();
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("CreateCardAuth"),hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("CreateCardAuth"));
    }

    return hresult;
}

/*++

CSCardManage::CreateFileAccess:

    Creates an instance of the ISCardFileAccess interface and returns
    a pointer to the created interface.

Arguments:

    pISCardFileAccess - pointer to ISCardFileAccess

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardManage::CreateFileAccess( LPSCARDFILEACCESS *ppISCardFileAccess )
{
    // Locals
    HRESULT     hresult = S_OK;
    LPSCARDFILEACCESS pObj = NULL;

    try {
        // Check params, etc..
        if (ppISCardFileAccess == NULL)
            throw ( (HRESULT) E_INVALIDARG );

        // Create an instance of the interface
        hresult = CoCreateInstance( CLSID_SCardFileAccess,
                                    NULL,
                                    CLSCTX_LOCAL,
                                    IID_ISCardFileAccess,
                                    (LPVOID*) &pObj);
        if (FAILED(hresult))
            throw (hresult);

        // Initialize the Interface/Object
        hresult = pObj->Initialize((LONG*) this);
        if (FAILED(hresult))
            throw (hresult);

        // Ok...
        (*ppISCardFileAccess) = pObj;
        // We are bumping the reference count on this object so we are sure it will
        // remain available. This means each object MUST Release() the count!
        AddRef();
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("CreateFileAccess"),hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("CreateFileAccess"));
    }

    return hresult;
}

/*++

CSCardManage::CreateInterface

    Creates an instance of an interface supported by this ssp.

Arguments:

    pISCardFileAccess - pointer to ISCardFileAccess

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardManage::CreateInterface( LPGUID pguidInterface,
                                            BSTR bstrName /*=NULL*/,
                                            LONG *pUserData /*=NULL*/,
                                            LPUNKNOWN *ppInterface)
{
    // Locals
    HRESULT     hresult = E_NOTIMPL;

    try {
        // Check params, etc..

        // Todo::Add code to support the creation of non-standard or
        // crypto interfaces
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("CreateInterface"),hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("CreateInterface"));
    }

    return hresult;
}

/*++

CSCardManage::CreateCHVerification:

    Creates an instance of the ISCardVerify interface and returns
    a pointer to the created interface.

Arguments:

    pISCardVerify - pointer to ISCardVerify

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardManage::CreateCHVerification( LPSCARDVERIFY *ppISCardVerify )
{
    // Locals
    HRESULT     hresult = S_OK;
    LPSCARDVERIFY pObj = NULL;

    try {
        // Check params, etc..
        if (ppISCardVerify == NULL)
            throw ( (HRESULT) E_INVALIDARG );

        // Create an instance of the interface
        hresult = CoCreateInstance( CLSID_SCardVerify,
                                    NULL,
                                    CLSCTX_LOCAL,
                                    IID_ISCardVerify,
                                    (LPVOID*) &pObj);
        if (FAILED(hresult))
            throw (hresult);

        // Initialize the Interface/Object
        hresult = pObj->Initialize((LONG*) this);
        if (FAILED(hresult))
            throw (hresult);

        // Ok...
        (*ppISCardVerify) = pObj;
        // We are bumping the reference count on this object so we are sure it will
        // remain available. This means each object MUST Release() the count!
        AddRef();
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("CreateCHVerify"),hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("CreateCHVerify"));
    }

    return hresult;
}

/*++

CSCardManage::Detach

    Releases the attachment to a particular ICC or IFD allocated by
    an AttachByxxx method

Arguments:

    none

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardManage::Detach()
{
    //locals
    HRESULT     hresult = S_OK;

    try {
        // Make low-level COM call
        hresult = m_pISCard->Detach(LEAVE);
        if (FAILED(hresult))
            throw (hresult);
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("Detach"),hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("Detach"));
    }

    return hresult;
}

/*++

CSCardManage::Locks:

    Locks the connected IFD/ICC for exclusive use by this application. Usually
    used for executing mutiple/sequence of commands to the card.

Arguments:

    none

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardManage::Lock()
{
    //locals
    HRESULT     hresult = S_OK;

    try {
        // Call low-level COM
        hresult = m_pISCard->LockSCard();
        if (FAILED(hresult))
            throw (hresult);
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("Lock"), hr);
    }

    catch (...) {
        hresult = E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("Lock"));
    }

    return hresult;
}

/*++

CSCardManage::Reconnect:

    Allows an application to reconnect to an ICC or IFD without having
    to issue a Detach then a AttachByxxx method call

Arguments:

    none

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardManage::Reconnect()
{
    //locals
    HRESULT     hresult = S_OK;

    try {
        // Call low-level COM
        hresult = m_pISCard->ReAttach(SHARED, LEAVE);
        if (FAILED(hresult))
            throw (hresult);
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("Reconnect"), hr);
    }

    catch (...) {
        hresult = E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("Reconnect"));
    }

    return hresult;
}

/*++

CSCardManage::Status:

    Allows an application to get the current status of the ICC/IFD.

Arguments:

    pwdStatus - Pointer to DWORD that will contain status
                    ABSENT
                    PRESENT
                    SWALLOWED
                    POWERED
                    NEGOTIABLEMODE
                    SPECIFICMODE
    pwdProtocol - point to DWORD that will contain current protocol in use
                    T0
                    T1
                    RAW

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardManage::Status(  SCARD_STATES    *pStatus,
                                    SCARD_PROTOCOLS *pProtocol)
{
    //locals
    HRESULT     hresult = S_OK;

    try {
        if ( (pStatus==NULL) || (pProtocol == NULL))
            throw ( (HRESULT) E_INVALIDARG );

        // Call low-level COM
        hresult = m_pISCard->get_Status(pStatus);
        if (FAILED(hresult))
            throw (hresult);
        hresult = m_pISCard->get_Protocol(pProtocol);
        if (FAILED(hresult))
            throw (hresult);
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("Status"), hr);
    }

    catch (...) {
        hresult = E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("Status"));
    }

    return hresult;
}

/*++

CSCardManage::Unlock:

    This method releases the connected IFD/ICC from exclusive use.

Arguments:

    Disposition - state to leave card in upon release.
        LEAVE
        RESET
        UNPOWER
        EJECT

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardManage::Unlock( SCARD_DISPOSITIONS  Disposition /*=LEAVE*/)
{
    //locals
    HRESULT     hresult = S_OK;

    try {
        // Call low-level COM
        hresult = m_pISCard->UnlockSCard(Disposition);
        if (FAILED(hresult))
            throw (hresult);
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("Unlock"), hr);
    }

    catch (...) {
        hresult = E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("Unlock"));
    }

    return hresult;
}


SCARDMAN.H

// SCardMan.h : Declaration of the CSCardManage

#ifndef __SCARDMANAGE_H_
#define __SCARDMANAGE_H_

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// CSCardManage
class ATL_NO_VTABLE CSCardManage :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CSCardManage, &CLSID_SCardManage>,
public IDispatchImpl<ISCardManage, &IID_ISCardManage, &LIBID_SCARDCOMLib>
{
public:
// Members

// Pointers to low level SSP
LPSCARD m_pISCard;
LPSCARDDATABASE m_pISCardDatabase;
LPSCARDLOCATE m_pISCardLocate;
LPSCARDISO7816 m_pISCardISO7816;
LPSCARDTYPECONV m_pISCardTypeConv;

CCriticalSection m_CriticalSection;

public:
CSCardManage(){}

~CSCardManage(){}

HRESULT FinalConstruct();

HRESULT FinalRelease();

//
//DECLARE_NOT_AGGREGATABLE(CSCardManage)
// Note:
// Remove the comment from the line above if you don't want your object to
// support aggregation.  The default is to support it
//

//Registry:

//Simple ALL 1.0 based registry entry
DECLARE_REGISTRY(CSCardManage,
_T("SCardCOM.SCardManage.1"),
_T("SCardCOM.SCardManage"),
IDS_SCARDMAN_DESC,
THREADFLAGS_APARTMENT)
// Note: Enters the standard class registration into the system
// registry.
//
// Script based...
// DECLARE_REGISTRY_RESOURCEID(IDR_SCARDMANAGE)

BEGIN_COM_MAP(CSCardManage)
COM_INTERFACE_ENTRY(ISCardManage)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

public:// ISCardManage
HRESULT CreateCmdObject(LPSCARDCMD* ppCMD);

public:
// ISCardManage
STDMETHOD(AttachByHandle)(
HSCARD hICC);
STDMETHOD(AttachByIFD)(
BSTR bstrIFDName,
SCARD_SHARE_MODES Flags );
STDMETHOD(CreateFileAccess)( LPSCARDFILEACCESS* ppISCardFileAccess );
STDMETHOD(CreateCardAuth)( LPSCARDAUTH* ppISCardAuth );
STDMETHOD(CreateInterface)(LPGUID pguidInterface,
BSTR bstrName,
LONG *pUserData,
LPUNKNOWN *ppInterface);
STDMETHOD(CreateCHVerification)( LPSCARDVERIFY* ppISCardVerify );
STDMETHOD(Detach)();
STDMETHOD(Lock)();
STDMETHOD(Reconnect)();
STDMETHOD(Status)(SCARD_STATES*pStatus,
SCARD_PROTOCOLS*pProtocol);
STDMETHOD(Unlock)(SCARD_DISPOSITIONS Disposition=LEAVE);
};

typedef CSCardManage* LPCSCARDMANAGE;

#endif //__SCARDMANAGE_H_


SCARDVER.CPP

// SCardVer.cpp : Implementation of CSCardVerify

//////////////////////////////////////////////////////////////////////////
//
// Includes
//
#include "stdafx.h"
#include "SCardCom.h"
#include "SCardVer.h"
#include "scarddef.h"

///////////////////////////////////////////////////////////////////////////
// Macros
//
#ifdef _DEBUG
#define TRACE_STR(name,sz) \
TRACE(_T("SCardCOM.DLL: CSCardVerify::%s: %s\n"), name, sz)
#define TRACE_CODE(name,code) \
TRACE(_T("SCardCOM.DLL: CSCardVerify::%s: error = 0x%x\n"), name, code)
#define TRACE_CATCH(name,code)TRACE_CODE(name,code)
#define TRACE_CATCH_UNKNOWN(name)TRACE_STR(name,_T("An unidentified exception has occurred!"))
#else
#define TRACE_STR(name,sz)((void)0)
#define TRACE_CODE(name,code)((void)0)
#define TRACE_CATCH(name,code)((void)0)
#define TRACE_CATCH_UNKNOWN(name)((void)0)
#endif  // _DEBUG

/////////////////////////////////////////////////////////////////////////////
// CSCardVerify

/*++

CSCardVerify::FinalConstruct:

    This routine defines a final constructor that is called after the constructor
for the template class is called.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

HRESULT CSCardVerify::FinalConstruct()
{
// Locals.
HRESULT hresult = S_OK;

try {
m_Manage = NULL;
}

catch (...) {
}

return (hresult);
}

/*++

CSCardVerify::FinalRelease:

    This routine defines a final release.

Arguments:

    None

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

HRESULT CSCardVerify::FinalRelease()
{
// Locals.
HRESULT hresult = S_OK;

try {
// Decrement the reference count on the "creation" object
if (m_Manage != NULL)
m_Manage->Release();
}

catch (...) {
}

return (hresult);
}

/////////////////////////////////////////////////////////////////////////////
//
// ISCardVerify Methods
//

/*++

CSCardVerify::ChangeCode

This method changes the current user code.

Arguments:

    pOldCode - contains the user's current code to be presented
to the ICC in the change process in order to authenticate the user

pNewCode - contains the NEW code to replace the current code.

Flags - indicates whether the code is global or local also whether the
code should be enabled or disabled.

dwRef - ICC specific reference

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardVerify::ChangeCode(
LPBYTEBUFFER pOldCode,
LPBYTEBUFFER pNewCode,
SCARD_FLAGS Flags,
LONG lRef)
{
//locals
HRESULThresult = E_NOTIMPL;

// to do
return hresult;
}

/*++

CSCardVerify::Initialize

Initializes the object for use.

Arguments:

lp - a long pointer to the "controlling" ISCardManage object.

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/
STDMETHODIMP CSCardVerify::Initialize( LONG *lp)
{
// Locals
HRESULThresult = S_OK;

try {
// Check Params, etc..
if (lp == NULL)
throw ( (HRESULT) E_INVALIDARG );

if (m_Manage != NULL)
throw ( (HRESULT) E_FAIL );

// Ok...
m_Manage = (LPCSCARDMANAGE) lp;
}

catch (HRESULT hr) {
hresult=hr;
TRACE_CATCH(_T("Initialize"), hr);
}

catch (...) {
hresult = (HRESULT) E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("Initialize"));
}

return (hresult);
}

/*++

CSCardVerify::Unblock

Unblocks a previously blocked IHV.

Arguments:

    dwData - contains vendor specific data to be used in the unblocking
process.

Flags - indicates whether the code is a global or local

dwRef - ICC specific reference

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardVerify::Unblock(
LONG lData,
SCARD_FLAGS Flags,
LONG lRef)
{
//locals
HRESULThresult = E_NOTIMPL;

// to do
return hresult;
}

/*++

CSCardVerify::ResetSecurityState

Resets either the global or local security context

Arguments:

    Flags - SC_FL_IHV_GLOBAL or SC_FL_IHV_LOCAL

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardVerify::ResetSecurityState(
SCARD_FLAGS Flags)
{
//locals
HRESULThresult = E_NOTIMPL;

// to do
return hresult;
}

/*++

CSCardVerify::Verify

Authenticate the user

Arguments:

    pCode - contains the Code to be presented to the ICC in the IHV
process.

Flags - indicates whether the code is global or local

lpRef - ICC specific reference

Return Value:

    A HRESULT value indicating the status of the requested action.

ReturnMeaning
=============
S_OKOperation completed successfully.
S_FALSEUnknown error occurred.
E_OUTOFMEMORYThere is not enough memory in this process to
satisfy the request.
E_FAILUnspecified error.
E_INVALIDARGOne or more arguments are invalid.
E_UNEXPECTEDUnexpected event occurred in this function.

Author:

--*/

STDMETHODIMP CSCardVerify::Verify(
LPBYTEBUFFER pCode,
SCARD_FLAGS Flags,
LONG* lpRef)
{
//locals
HRESULThresult = S_OK;
BYTEbyReferenceControl;
LPSCARDCMDpCmd = NULL;

try {
// Is internal pointer to "Creation Class" valid
if (m_Manage == NULL)
throw ( (HRESULT) E_FAIL );

// Check params...
if (pCode == NULL)
throw ( (HRESULT) E_POINTER );

// Create the p1-p2 paramters..In this case assume the verification (i.e. PIN,
// etc) is to be passed in as pCode
if (Flags == SC_FL_IHV_GLOBAL)
byReferenceControl = SC_GLOBAL_REF;
else
byReferenceControl = SC_SPECIFIC_REF;

// ISO7186 COM builds command
hresult = m_Manage->m_pISCardISO7816->Verify(byReferenceControl,
pCode,
&pCmd);

if (FAILED(hresult))
throw(hresult);

// Force correct class id for the vendor's card
hresult = pCmd->put_ClassId(VENDOR_CLASS_ID);
if (FAILED(hresult))
throw (hresult);

hresult = m_Manage->m_pISCard->LockSCard();
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->Transaction(&pCmd);
if (FAILED(hresult))
throw (hresult);
hresult = m_Manage->m_pISCard->UnlockSCard(LEAVE);
if (FAILED(hresult))
throw (hresult);

//interpret results and return
hresult = pCmd->get_ApduReplyLength(&m_lReplyLength);
if (FAILED(hresult))
throw (hresult);
hresult = pCmd->get_ReplyStatus( &m_wReplyStatus );
if (FAILED(hresult))
throw (hresult);
if (m_wReplyStatus != 0x9000)
throw ( (HRESULT) E_FAIL);
}

catch (HRESULT hr){
hresult = hr;
TRACE_CATCH(_T("Verify"), hr);
}

catch (...){
hresult = E_UNEXPECTED;
TRACE_CATCH_UNKNOWN(_T("Verify"));
}

if (pCmd != NULL)
pCmd->Release();

return hresult;
}


SCARDVER.H

// SCardVer.h : Declaration of the CSCardVerify

#ifndef __SCARDVERIFY_H_
#define __SCARDVERIFY_H_

#include "resource.h"       // main symbols
#include "scardman.h"

/////////////////////////////////////////////////////////////////////////////
// CSCardVerify
class ATL_NO_VTABLE CSCardVerify :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CSCardVerify, &CLSID_SCardVerify>,
public IDispatchImpl<ISCardVerify, &IID_ISCardVerify, &LIBID_SCARDCOMLib>
{
private:
// Attributes:
LPCSCARDMANAGEm_Manage;
longm_lAlgorID;
LONGm_lReplyLength;
WORDm_wReplyStatus;
CCriticalSectionm_CriticalSection;

public:
CSCardVerify(){}

~CSCardVerify(){}

HRESULT FinalConstruct();

HRESULT FinalRelease();

//
//DECLARE_NOT_AGGREGATABLE(CSCardFileAccess)
// Note:
// Remove the comment from the line above if you don't want your object to
// support aggregation.  The default is to support it
//

//Registry:

//Simple ALL 1.0 based registry entry
DECLARE_REGISTRY(CSCardVerify,
_T("SCardCOM.SCardVerify.1"),
_T("SCardCOM.SCardVerify"),
IDS_SCARDVER_DESC,
THREADFLAGS_APARTMENT)
// Note: Enters the standard class registration into the system
// registry.
//
// Script based...
// DECLARE_REGISTRY_RESOURCEID(IDR_SCARDVERIFY)

BEGIN_COM_MAP(CSCardVerify)
COM_INTERFACE_ENTRY(ISCardVerify)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

public:
// ISCardVerify Methods
STDMETHOD(ChangeCode)(
LPBYTEBUFFER pOldCode,
LPBYTEBUFFER pNewCode,
SCARD_FLAGS Flags,
LONG lRef);
STDMETHOD(Initialize)(
LONG* lp);
STDMETHOD(Unblock)(
LONG lData,
SCARD_FLAGS Flags,
LONG lRef);
STDMETHOD(ResetSecurityState)(
SCARD_FLAGS Flags);
STDMETHOD(Verify)(
LPBYTEBUFFER pCode,
SCARD_FLAGS Flags,
LONG* lpRef);
};

#endif //__SCARDVERIFY_H_


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//  stdafx.pch will be the pre-compiled header
//  stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

#ifdef _ATL_STATIC_REGISTRY
#include <statreg.h>
#include <statreg.cpp>
#endif

#include <atlimpl.cpp>


STDAFX.H

// stdafx.h : include file for standard system include files,
//      or project specific include files that are used frequently,
//      but are changed infrequently

#if !defined(AFX_STDAFX_H__410100D5_B80D_11D0_B893_00C04FD424B9__INCLUDED_)
#define AFX_STDAFX_H__410100D5_B80D_11D0_B893_00C04FD424B9__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#define STRICT

#include <afxwin.h>
#include <afxdisp.h>
// Include the template class(s)
#include <afxtempl.h>

#define _WIN32_WINNT 0x0400
#define _ATL_APARTMENT_THREADED

#ifndef __LOWLEVEL_SSP_COM__
#define __LOWLEVEL_SSP_COM__
#include <scarddat.h>
#include <scardmgr.h>
#include <scardsrv.h>
#endif

// Include the COM types
#include <sspsidl.h>
#include <sspserr.h>

// Include Helpers
#include "wrphelp.h"

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__410100D5_B80D_11D0_B893_00C04FD424B9__INCLUDED)


WRPHELP.CPP

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    WrpHelp

Abstract:

    This file contains wrapper class implementations.

Author:

Environment:

    Win32, C++ w/Exceptions, ATL, COM/OLE

Notes:

--*/

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//

#include "stdafx.h"
#include "wrphelp.h"
#include "scarddef.h"

///////////////////////////////////////////////////////////////////////////
// Macros
//
#ifdef _DEBUG
    #define TRACE_STR(name,sz) \
                TRACE(_T("WrpHelp Classes::%s: %s\n"), name, sz)
    #define TRACE_CODE(name,code) \
                TRACE(_T("WrpHelp Classes::%s: error = 0x%x\n"), name, code)
    #define TRACE_CATCH(name,code)      TRACE_CODE(name,code)
    #define TRACE_CATCH_UNKNOWN(name)   TRACE_STR(name,_T("An unidentified exception has occurred!"))
#else
    #define TRACE_STR(name,sz)          ((void)0)
    #define TRACE_CODE(name,code)       ((void)0)
    #define TRACE_CATCH(name,code)      ((void)0)
    #define TRACE_CATCH_UNKNOWN(name)   ((void)0)
#endif  // _DEBUG

///////////////////////////////////////////////////////////////////////////////
//
// Construction and Destruction
//

/*++

CCriticalSection::CCriticalSection

    This method implements the constructor for CCriticalSection

Arguments:

    None

Return Value:

    None

Author:

--*/

CCriticalSection::CCriticalSection()
{
    // locals

    // Initialize the internal critical section object
    try {
        ::InitializeCriticalSection(&m_CriticalSection);
    }

    catch (...){
        TRACE_CATCH_UNKNOWN(_T("CCriticalSection"));
    }
}

/*++

CCriticalSection::~CCriticalSection

    This method implements the destructor for CCriticalSection

Arguments:

    None

Return Value:

    None

Author:

--*/
CCriticalSection::~CCriticalSection()
{
    // locals

    try {
        ::DeleteCriticalSection(&m_CriticalSection);
    }

    catch(...) {
        TRACE_CATCH_UNKNOWN(_T("~CriticalSection"));
    }
}

/////////////////////////////////////////////////////////////////////////////////////
//
// CCriticalSection
//

/*++

CCriticalSection::EnterCriticalSection

    This routines puts the internal critical section
    object into the non-signaled state

Arguments:

    None

Return Value:

    None

Author:

--*/
CCriticalSection::EnterCriticalSection()
{
    // Locals
    BOOL    fResult = FALSE;

    try {
        ::EnterCriticalSection(&m_CriticalSection);

        // Ok...
        fResult = TRUE;
    }

    catch (...) {
        TRACE_CATCH_UNKNOWN(_T("EnterCriticalSection"));
    }

    return fResult;
}

/*++

CCriticalSection::ExitCriticalSection

    This routines puts the internal critical section
    object into the signaled state

Arguments:

    None

Return Value:

    None

Author:

    Chris Dudley (cdudley) 2/6/1997

--*/
CCriticalSection::ExitCriticalSection()
{
    // Locals
    BOOL        fResult = FALSE;

    try {
        ::LeaveCriticalSection(&m_CriticalSection);

        // Ok...
        fResult = TRUE;
    }

    catch (...) {
        TRACE_CATCH_UNKNOWN(_T("EnterCriticalSection"));
    }

    return fResult;
}

///////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////
//
// CGrabData
//

/*++

CGrabData::GetExtraData

    This routine calls the ISO7816-4 command GetResponse to get additional
    data from the card and return it in the IByteBuffer (aka IStream)

Arguments:

    ppDataOut - pointer to a pointer to an IByteBuffer. If this pointer is
                NULL, the IStream will be created but MUST be release in caller's
                code.
    pwCmdStatus - (IN) The status word (SW1/SW2) returned from the initial smart card
                command.
                 (OUT) the final status word.
    pdwDataLength - (OUT) Total number of bytes written into the buffer.

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return                      Meaning
    ======                      =======
    E_INVALIDARG                An argument was invalid or unrecognizable.
    E_OUTOFMEMORY               No free memory in process address space
                                to create requested object.
    E_UNEXPECTED                One of the parameters was of an unexpected
                                type or value.
    S_OK                        Command successful.

Author:

Notes:

    This routine expects (*pwCmdStatus) to contain a 0x61xx response code where
    xx is the number of bytes to be retrieved by the GetResponse.

--*/
HRESULT CGrabData::GetExtraData(LPBYTEBUFFER *ppDataOut,
                                WORD *pwCmdStatus,
                                LONG *plDataLength)
{
    // Locals
    HRESULT         hresult = S_OK;
    LONG            lSize;
    LPBYTEBUFFER    pBuffer;
    ULARGE_INTEGER  liBytesToCopy,
                    liBytesRead,
                    liBytesWritten,
                    liLength;

    try {
        // clear length
        (*plDataLength) = 0;
        // Create buffers if needed...1 for GetResponse, another to copy into
        if ( (*ppDataOut) == NULL) {
            hresult = m_pISCardTypeConv->CreateByteBuffer((ULONG) 1,
                                                        ppDataOut);
            if (FAILED(hresult))
                throw (hresult);
        };// if
        hresult = m_pISCardTypeConv->CreateByteBuffer((ULONG) 1,
                                                        &pBuffer);
        if (FAILED(hresult))
            throw (hresult);
        // Check status
        m_wReplyStatus = (*pwCmdStatus);
        while(HIBYTE(m_wReplyStatus) == SC_STATUS_MOREDATA){
            lSize = LOBYTE( m_wReplyStatus );
            // Build the command
            hresult = m_pISCardISO7816->GetResponse((BYTE) 0,
                                                    (BYTE) 0,
                                                    lSize,
                                                    &m_pISCardCmd);
            if (FAILED(hresult))
                throw (hresult);

            // Force correct class id for the vendor's card
            hresult = m_pISCardCmd->put_ClassId(VENDOR_CLASS_ID);
            if (FAILED(hresult))
                throw (hresult);

            // Do transaction
            hresult = m_pISCard->LockSCard();
            if (FAILED(hresult))
                throw (hresult);
            hresult = m_pISCard->Transaction(&m_pISCardCmd);
            if (FAILED(hresult))
                throw (hresult);
            hresult = m_pISCard->UnlockSCard(LEAVE);
            if (FAILED(hresult))
                throw (hresult);
            // Interpret response
            hresult = m_pISCardCmd->get_ApduReplyLength(&m_lReplyLength);
            if (FAILED(hresult))
                throw (hresult);
            hresult = m_pISCardCmd->get_ReplyStatus( &m_wReplyStatus );
            if (FAILED(hresult))
                throw (hresult);
            if ( (m_wReplyStatus != SC_STATUS_SUCCESS) &&
                    (HIBYTE(m_wReplyStatus) != SC_STATUS_MOREDATA ))
                throw ( (HRESULT) E_FAIL);

            // Get reply data
            hresult = m_pISCardCmd->get_ApduReply( &pBuffer );
            if (FAILED(hresult))
                throw (hresult);

            // Copy it into given Internal IStream. First set new Length.
            // Don't REALLY have to do this. The IStream Copy would take care of it...
            (*plDataLength) += m_lReplyLength;
            LISet32 (liLength, (*plDataLength));
            hresult = (*ppDataOut)->SetSize((LONG) liLength.LowPart);
            if (FAILED(hresult))
                throw (hresult);
            LISet32 (liBytesToCopy, m_lReplyLength );
            hresult = (pBuffer)->CopyTo(ppDataOut,
                                        (LONG) liBytesToCopy.LowPart,
                                        (LONG*) &(liBytesRead.LowPart),
                                        (LONG*) &(liBytesWritten.LowPart));
            if (FAILED(hresult))
                throw (hresult);
        }; //while
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("GetResultData"),hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("GetResultData"));
    }

    // Release resources...Cleanup
    if (pBuffer != NULL)
        while (pBuffer->Release()>0);

    (*pwCmdStatus) = m_wReplyStatus;

    return hresult;
}

//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
//
//  Various Useful routines
//

/*++

SspCreateByteArray:

    This routine creates an array of bytes.

Arguments:

    dwAllocSize - Size in bytes of the memory to be allocated for the array.

    ppbyArray - Points to the created byte array to be returned.

    pExceptInfo - Points to an OLE exception block which contains extended
        information about any failure that might occur. Can be set to
        NULL if the exception information is not desired.

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            The byte array was created successfully.
    S_FALSE         Unknown error.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_HANDLE        Invalid handle.
    E_POINTER       Invalid pointer.
    E_UNEXPECTED    Unexpected fault has occurred.

Author:

--*/

HRESULT
SspCreateByteArray(
    IN DWORD dwAllocSize,
    OUT LPBYTE *ppbyArray,
    OUT OPTIONAL EXCEPINFO* pExceptInfo)
{
    // Locals.
    HRESULT hr = S_OK;
    HGLOBAL hMem = NULL;
    LPBYTE pObj = NULL;
    DWORD dwErr = NO_ERROR;

    try
    {
        // Initialize the parameters.
        (*ppbyArray) = NULL;

        // Is caller asking for a reasonable array size?
        if (dwAllocSize <= 0)
            throw ( (HRESULT) E_INVALIDARG);

        // Allocate the memory for the array from the system.
        hMem = ::GlobalAlloc(GHND, dwAllocSize);
        if (hMem == NULL)
        {
            dwErr = ::GetLastError();
            throw (HRESULT_FROM_WIN32(dwErr));
        }

        // Map the memory for use.
        pObj = (LPBYTE) GlobalLock(hMem);
        if (pObj == NULL)
        {
            dwErr = ::GetLastError();
            throw (HRESULT_FROM_WIN32(dwErr));
        }
        ASSERT(pObj != NULL);

        // Ok, pack for return.
        (*ppbyArray) = pObj;
    }

    catch (HRESULT hrStatus)
    {
        hr = hrStatus;
        TRACE_CATCH(_T("SspCreateByteArray"),hrStatus);
        if (pObj!=NULL)
            ::GlobalUnlock(hMem);
        if (hMem!=NULL)
            ::GlobalFree(hMem);
        switch (hr)
        {
            case E_UNEXPECTED:
            case E_POINTER:
            case E_INVALIDARG:
                ::SetLastError( (DWORD) ERROR_INVALID_PARAMETER);
                break;

            case S_FALSE:
            case E_FAIL:
                ::SetLastError((DWORD) ERROR_NOT_SUPPORTED);
                break;

            case E_ACCESSDENIED:
                ::SetLastError((DWORD)ERROR_ACCESS_DENIED);
                break;

            case E_HANDLE:
                ::SetLastError((DWORD) ERROR_INVALID_HANDLE);
                break;

            case E_OUTOFMEMORY:
                ::SetLastError((DWORD) ERROR_OUTOFMEMORY);
                break;

            default:
                ::SetLastError(dwErr);
                break;
        }
    }

    catch (...)
    {
        TRACE_CATCH_UNKNOWN(_T("SspCreateByteArray"));
        if (pObj != NULL)
            ::GlobalUnlock(hMem);
        if (hMem != NULL)
            ::GlobalFree(hMem);
        ::SetLastError( (DWORD) ERROR_INVALID_PARAMETER);
        hr = (HRESULT) E_UNEXPECTED;
    }

    return (hr);
}

/*++

SspConvertDwordToByte:

    This function will convert a DWORD to an array of BYTEs.

Arguments:

    dwNum - DWORD number to be converted.

    ppbyArray - Points to the byte array created to hold the converted
        DWORD if successful; else, NULL!

    pdwByteArraySize - Optional pointer to how big was the byte array
        created to hold the DWORD is.

    pExceptInfo - Points to an OLE exception block which contains extended
        information about any failure that might occur. Can be set to
        NULL if the exception information is not desired.

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return              Meaning
    ======              =======
    S_OK                Operation completed successfully.
    S_FALSE             Unknown error.
    E_OUTOFMEMORY       There is not enough memory in this process to
                        satisfy the request.
    E_FAIL              Unspecified error.
    E_INVALIDARG        One or more arguments are invalid.
    E_POINTER           Invalid pointer.
    E_UNEXPECTED        Unexpected event occurred in this function.
    E_HANDLE            Invalid handle.
    E_ACCESSDENIED      Access to resource is denied.

Author:

--*/

HRESULT
SspConvertDwordToByte(
    IN DWORD dwNum,
    OUT LPBYTE *ppbyArray,
    OUT LPDWORD pdwByteArraySize,
    OUT OPTIONAL EXCEPINFO* pExceptInfo)
{
    // Locals.
    LPBYTE pbyBuff = NULL;
    HRESULT hr = S_OK;
    DWORD dwSize = 0;

    try
    {
        // Initialize...
        if ((*ppbyArray))
            (*ppbyArray) = NULL;
        if (pdwByteArraySize)
            (*pdwByteArraySize) = 0;

        // Determine the appropriately sized byte array needed
        // to hold DWORD.
        if (dwNum <= 0xFF)
            dwSize = 1;
        else if (dwNum <= 0xFFFF)
                dwSize = 2;
        else if (dwNum <= 0xFFFFFF)
                dwSize = 3;
        else
            throw ( (HRESULT) E_INVALIDARG);

        // Create the byte array.
        hr = SspCreateByteArray(dwSize,
                                &pbyBuff,
                                NULL);
        if (FAILED(hr))
            throw (hr);
        if (pbyBuff == NULL)
            throw ( (HRESULT) E_OUTOFMEMORY);
        ASSERT(pbyBuff != NULL);

        // Copy it!
        ::CopyMemory( (LPVOID) pbyBuff,
                      (CONST VOID*) &dwNum,
                      (DWORD) sizeof(DWORD) );

        // Package for return.
        *ppbyArray = pbyBuff;
        if (pdwByteArraySize != NULL)
            (*pdwByteArraySize) = dwSize;
    }

    catch (HRESULT hrStatus)
    {
        hr = hrStatus;
        TRACE_CATCH(_T("SspConvertDwordToByte"),hrStatus);
        switch (hr)
        {
            case E_UNEXPECTED:
            case E_POINTER:
            case E_INVALIDARG:
                ::SetLastError((DWORD) ERROR_INVALID_PARAMETER);
                break;

            case S_FALSE:
            case E_FAIL:
                ::SetLastError((DWORD) ERROR_NOT_SUPPORTED);
                break;

            case E_ACCESSDENIED:
                ::SetLastError((DWORD) ERROR_ACCESS_DENIED);
                break;

            case E_HANDLE:
                ::SetLastError((DWORD) ERROR_INVALID_HANDLE);
                break;

            case E_OUTOFMEMORY:
                ::SetLastError((DWORD) ERROR_OUTOFMEMORY);
                break;
        }
    }

    catch (...)
    {
        TRACE_CATCH_UNKNOWN(_T("SspConvertDwordToByte"));
        ::SetLastError((DWORD) ERROR_INVALID_PARAMETER);
        hr = (HRESULT) E_UNEXPECTED;
    }

    return (hr);
}

/*++

SspFreeByteArray:

    This routine frees a byte array (aka a block of HGLOBAL memory).

Arguments:

    pbyArray - Points to the byte array to be freed.

    pExceptInfo - Points to an OLE exception block which contains extended
        information about any failure that might occur. Can be set to
        NULL if the exception information is not desired.

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            The byte array was created freed.
    S_FALSE         Unknown error.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected fault has occurred.
    E_POINTER       Invalid pointer.
    E_HANDLE        Invalid handle.
    E_ACCESSDENIED  Access to resource is denied.

Author:

--*/

HRESULT
SspFreeByteArray(
    IN LPBYTE pbyArray,
    OUT OPTIONAL EXCEPINFO* pExceptInfo)
{
    // Locals.
    DWORD dwErr = NO_ERROR;
    HGLOBAL hMem = NULL;
    HRESULT hr = S_OK;

    try
    {
        // Is the parameter-list ok?
        if (pbyArray == NULL)
            throw ( (HRESULT) E_POINTER);

        // Get access to the string's memory block.
        hMem = ::GlobalHandle((LPCVOID) pbyArray);
        if (hMem == NULL)
        {
            dwErr = ::GetLastError();
            throw (HRESULT_FROM_WIN32(dwErr));
        }
        ASSERT(hMem != NULL);

        // Decrement the memory block's reference count.
        while (::GlobalUnlock(hMem));
        dwErr = ::GetLastError();
        if (dwErr != NO_ERROR)
            throw (HRESULT_FROM_WIN32(dwErr));

        // Free the memory!
        hMem = ::GlobalFree(hMem);
        if (hMem == NULL)
        {
            dwErr = ::GetLastError();
            throw (HRESULT_FROM_WIN32(dwErr));
        }
        ASSERT(hMem == NULL);
    }

    catch (HRESULT hrStatus)
    {
        hr = hrStatus;
        TRACE_CATCH(_T("SspFreeByteArray"),hrStatus);
        switch (hr)
        {
            case E_UNEXPECTED:
            case E_POINTER:
            case E_INVALIDARG:
                ::SetLastError((DWORD) ERROR_INVALID_PARAMETER);
                break;

            case S_FALSE:
            case E_FAIL:
                ::SetLastError((DWORD) ERROR_NOT_SUPPORTED);
                break;

            case E_ACCESSDENIED:
                ::SetLastError((DWORD) ERROR_ACCESS_DENIED);
                break;

            case E_HANDLE:
                ::SetLastError((DWORD) ERROR_INVALID_HANDLE);
                break;

            case E_OUTOFMEMORY:
                ::SetLastError((DWORD) ERROR_OUTOFMEMORY);
                break;

            default:
                ::SetLastError(dwErr);
                break;
        }
    }

    catch (...)
    {
        TRACE_CATCH_UNKNOWN(_T("SspFreeByteArray"));
        ::SetLastError((DWORD) ERROR_INVALID_PARAMETER);
        hr = (HRESULT) E_UNEXPECTED;
    }

    return (hr);
}


WRPHELP.H

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    WrpHelp

Abstract:

This file contains the class declarations for wrapper helper classes

Author:

Environment:

    Win32, C++ w/Exceptions, ATL, COM/OLE

Revision History:

Notes:

--*/

#ifndef __WRPHELP_H__
#define __WRPHELP_H__

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//
#ifndef __LOWLEVEL_SSP_COM__
#define __LOWLEVEL_SSP_COM__
#include <scarddat.h>
#include <scardmgr.h>
#include <scardsrv.h>
#endif

/////////////////////////////////////////////////////////////////////////////
//
// Class Declaration
//
class CCriticalSection
{
// Members
private:

CRITICAL_SECTIONm_CriticalSection;

public:

// Construction/Destruction
public:
CCriticalSection();

~CCriticalSection();

// Methods
BOOL EnterCriticalSection();
BOOL ExitCriticalSection();
};

/////////////////////////////////////////////////////////////////////////////
//
// Class Declaration
//
class CGrabData
{
// members
private:
LPSCARDCMDm_pISCardCmd;
LPSCARDm_pISCard;
LPSCARDISO7816m_pISCardISO7816;
LPSCARDTYPECONV m_pISCardTypeConv;
LONGm_lReplyLength;
WORDm_wReplyStatus;

public:

public:

// Construction/Destruction
CGrabData(LPSCARDCMD pISCardCmd,
  LPSCARD pISCard, LPSCARDISO7816 pISCardIso,
  LPSCARDTYPECONV pISCardTypeConv) {
if ( (pISCardCmd == NULL) || (pISCard == NULL)
 || (pISCardIso == NULL) || (pISCardTypeConv == NULL) )
delete (this);
m_pISCardCmd = pISCardCmd;
m_pISCard = pISCard;
m_pISCardISO7816 = pISCardIso;
m_pISCardTypeConv = pISCardTypeConv;
}

~CGrabData() {}

// Methods
HRESULT GetExtraData(LPBYTEBUFFER *ppDataOut,
WORD *pwCmdStatus,
LONG *plDataLength);
};

//////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////
//
// Other helpful routines
//
HRESULT
SspCreateByteArray(
IN DWORD dwAllocSize,
OUT LPBYTE *ppbyArray,
OUT OPTIONAL EXCEPINFO* pExceptInfo);

HRESULT
SspConvertDwordToByte(
IN DWORD dwNum,
OUT LPBYTE *ppbyArray,
OUT LPDWORD pdwByteArraySize,
OUT OPTIONAL EXCEPINFO* pExceptInfo);

HRESULT
SspFreeByteArray(
IN LPBYTE pbyArray,
OUT OPTIONAL EXCEPINFO* pExceptInfo);

#endif // __CRITSEC_H__


SCARDWRP.CPP

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    scardwrp

Abstract:

    This file contains a simple class implementation for "wrapping" some
    of the Vendor COM Wrapper API functionality.

Author:

Environment:

    Win32, C++ w/Exceptions, ATL, COM/OLE

--*/

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//
#include "preapp.h"   //This include for the app SSP COM example
#include "SCardWrp.h"

// GUIDs
#include <WrpGUID.h>
#include <sspguid.h>

// OLE Specific headers
#include <ole2.h>
#include <objbase.h>
#include <oleauto.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////////
// Macros
//
#ifdef _DEBUG
    #define TRACE_STR(name,sz) \
                TRACE(_T("CSCardWrapper::%s: %s\n"), name, sz)
    #define TRACE_CODE(name,code) \
                TRACE(_T("CSCardWrapper::%s: error = 0x%x\n"), name, code)
    #define TRACE_CATCH(name,code)      TRACE_CODE(name,code)
    #define TRACE_CATCH_UNKNOWN(name)   TRACE_STR(name,_T("An unidentified exception has occurred!"))
#else
    #define TRACE_STR(name,sz)          ((void)0)
    #define TRACE_CODE(name,code)       ((void)0)
    #define TRACE_CATCH(name,code)      ((void)0)
    #define TRACE_CATCH_UNKNOWN(name)   ((void)0)
#endif  // _DEBUG

////////////////////////////////////////////////////////////////////////////
//
// CSCardWrapper
//

/*++

CSCardWrapper::CreateByteBuffer

    This routines creates a byte buffer.

Arguments:

    dwSize - size of bytebuffer to create
    ppBuffer - pointer to created buffer

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

--*/
HRESULT CSCardWrapper::CreateByteBuffer(DWORD dwSize,
                                        LPBYTEBUFFER *ppBuffer)
{
    //locals
    HRESULT     hresult = S_OK;
    LPCLASSFACTORY  lpClassFactory = NULL;

    try {
        // Check Parames, etc
        if (ppBuffer == NULL)
            throw ( (HRESULT) E_INVALIDARG );

        // Do WIN 95 safe creation of the IByteBuffer IStream wrapper
        hresult = CoGetClassObject( CLSID_ByteBuffer,
                                    CLSCTX_LOCAL,
                                    NULL,
                                    IID_IClassFactory,
                                    (LPVOID*) &lpClassFactory );
        if (FAILED(hresult))
            throw (hresult);
        ASSERT(lpClassFactory != NULL);
        hresult = lpClassFactory->CreateInstance(NULL,
                                                IID_IByteBuffer,
                                                (LPVOID*) ppBuffer);
        if (FAILED(hresult))
            throw (hresult);
        ASSERT( (*ppBuffer) != NULL);

        // Initialize the IStream wrapper
        hresult = (*ppBuffer)->Initialize(dwSize, NULL);
        if (FAILED(hresult))
            throw (hresult);
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("CreateByteBuffer"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("CreateByteBuffer"));
    }

    return hresult;
}

/*++

CSCardWrapper::AttachToReaderByName

    This routine attaches to a reader (and the card in that reader)

Arguments:

    szReaderName = a null terminated wide character string
    containing the reader name

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

--*/
HRESULT CSCardWrapper::AttachToReaderByName( WCHAR  *wszReaderName )
{
    //locals
    HRESULT     hresult = S_OK;
    BSTR        bstrReader = NULL;

    try {
        if (!m_sequence.COMLoad)
            throw ( (HRESULT) E_FAIL);
        if (wszReaderName == NULL)
            throw ( (HRESULT) E_INVALIDARG );
        // create the BSTR
        bstrReader = ::SysAllocString(wszReaderName);
        if (bstrReader == NULL)
            throw ( (HRESULT) E_OUTOFMEMORY );
        // Attach to reader
        hresult=m_pISCardManage->AttachByIFD( bstrReader, SHARED );
        if (FAILED(hresult))
            throw(hresult);

        // Ok...
        m_sequence.Attach = 1;
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("AttachToReaderByName"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("AttachToReaderByName"));
    }

    // Free memory
    if (bstrReader != NULL)
        ::SysFreeString(bstrReader);

    return hresult;
}

/*++

CSCardWrapper::ChangeDirectory

    Changes to the specified directory on the ICC.

Arguments:

    wszDirectory = a null terminated wide character string
    containing the Directory

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

    This method is calling the ISO 7816-4 SelectFile command. The wszDirectory
    should ONLY be a 2-byte file ID (as supported by the vendor com example code).

--*/
HRESULT CSCardWrapper::ChangeDirectory( WCHAR *wszDirectory )
{
    //locals
    HRESULT     hresult = S_OK;
    BSTR        bstrDir = NULL;

    try {
        // Are attached?
        if (!m_sequence.Attach)
            throw ( (HRESULT) E_FAIL );
        if (wszDirectory == NULL)
            throw ( (HRESULT) E_INVALIDARG );
        //Create BSTR
        bstrDir = ::SysAllocString(wszDirectory);
        if (bstrDir == NULL)
            throw ( (HRESULT) E_OUTOFMEMORY);
        //ChangeDir
        hresult=m_pISCardFileAccess->ChangeDir( SC_TYPE_BY_ID,
                                                bstrDir);
        if (FAILED(hresult))
            throw(hresult);

        //Ok...
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("ChangeDirectory"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("ChangeDirectory"));
    }

    // Free memory
    if (bstrDir != NULL)
        ::SysFreeString(bstrDir);

    return hresult;
}

/*++

CSCardWrapper::Detach

    This method does a detach from the current card.

Arguments:

    None.

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

--*/
HRESULT CSCardWrapper::Detach()
{
    // Locals
    HRESULT     hresult = S_OK;

    try {
        hresult = m_pISCardManage->Detach();
        if (FAILED(hresult))
            throw (hresult);
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("Detach"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("Detach"));
    }

    return hresult;
}

/*++

CSCardWrapper::GetChallenge

    This method calls gets a challenge from the ICC and returns

Arguments:

    lpbyChallange - long point to a byte array large enough to hold the
    challenge.

    lpLengthOfChal - length of the challenge expected.

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

--*/
HRESULT CSCardWrapper::GetChallenge( LPBYTE lpbyChallenge, LONG* lpLengthOfChal )
{
    // locals
    HRESULT         hresult = S_OK;
    LPBYTEBUFFER    pOutputBuffer = NULL;
    ULONG           ulBytesRead;
    LARGE_INTEGER   li;

    try {
        if (!m_sequence.Attach)
            throw ( (HRESULT) E_FAIL );
        //Get challenge..NOTE Vendor COM will create outputbuffer but we must
        // ->Release!!
        hresult = m_pISCardAuth->GetChallenge(  0L,
                                                NULL,
                                                lpLengthOfChal,
                                                &pOutputBuffer);
        if (FAILED(hresult))
            throw hresult;
        // set seek position/Read challenge head
        LISet32 (li, BYTE_BUFFER_HEAD );
        hresult = pOutputBuffer->Seek(  (LONG) li.LowPart,
                                        (LONG) STREAM_SEEK_SET,
                                        NULL);
        if (FAILED(hresult))
            throw hresult;
        // Read data into return stream
        hresult = pOutputBuffer->Read(  (BYTE*) lpbyChallenge,
                                        (LONG)*lpLengthOfChal,
                                        (LONG*)&ulBytesRead);
        if (FAILED(hresult))
            throw hresult;
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("GetChallenge"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("GetChallenge"));
    }

    // Release Bytebuffer (IStream) object
    if (pOutputBuffer != NULL)
        while (pOutputBuffer->Release() > 0);

    return hresult;
}

/*++

CSCardWrapper::LoadCOM

    This routine loads the Vendor COM object which, in turn, loads
    the low level COM API's

Arguments:

    none

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

--*/

HRESULT CSCardWrapper::LoadCOM()
{
    // locals
    HRESULT     hresult = S_OK;

    try {
        // Initialize COM
        hresult = CoInitialize(NULL);
        // SCardManage
        hresult = CoCreateInstance( CLSID_SCardManage,
                                    NULL,
                                    CLSCTX_INPROC_SERVER,
                                    IID_ISCardManage,
                                    (void FAR * FAR *) &m_pISCardManage);
        if ( FAILED(hresult) )
            throw (hresult);

        // Use the SCardManage object to create the others
        hresult = m_pISCardManage->CreateCardAuth(  &m_pISCardAuth );
        if (FAILED(hresult))
            throw (hresult);

        hresult = m_pISCardManage->CreateFileAccess( &m_pISCardFileAccess );
        if (FAILED(hresult))
            throw (hresult);

        hresult = m_pISCardManage->CreateCHVerification( &m_pISCardVerify );
        if (FAILED(hresult))
            throw (hresult);

        //ok...
        m_sequence.COMLoad = 1;
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("Initialize"),hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("Initialize"));
    }

    //

    return hresult;
}

/*++

CSCardWrapper::Open

    This method opens the requested file.

Arguments:

    wszFilename - 0 terminated, wide character string

    phFile - pointer to an HSCARD_FILE ICC file handle

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

--*/
HRESULT CSCardWrapper::Open( WCHAR *wszFilename, HSCARD_FILE *phFile )
{
    // locals
    HRESULT     hresult = S_OK;
    BSTR        bstrFilename = NULL;

    try {
        if ( (wszFilename == NULL) || (phFile == NULL) )
            throw ( (HRESULT) E_INVALIDARG);
        bstrFilename = ::SysAllocString( wszFilename );
        if (bstrFilename == NULL)
            throw ( (HRESULT) E_OUTOFMEMORY );
        // Open the file
        hresult = m_pISCardFileAccess->Open(SC_TYPE_BY_ID,
                                            bstrFilename,
                                            phFile);
        if (FAILED(hresult))
            throw (hresult);

        // Ok...
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("Open"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("Open"));
    }

    // Free memory
    if (bstrFilename != NULL)
        ::SysFreeString(bstrFilename);

    return hresult;
}

/*++

CSCardWrapper::Status

    This method returns status of card.

Arguments:

    pState - State of the card
    pProtocol - Protocol in use on the card

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

--*/
HRESULT CSCardWrapper::Status( SCARD_STATES *pState, SCARD_PROTOCOLS *pProtocol )
{
    //locals
    HRESULT     hresult = S_OK;

    try {
        if (!m_sequence.Attach)
            throw ( (HRESULT) E_FAIL );
        if ( (pState == NULL) || (pProtocol == NULL) )
            throw ( (HRESULT) E_INVALIDARG );
        // Get Status
        hresult = m_pISCardManage->Status(pState, pProtocol);

        //Ok...
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("Status"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("Status"));
    }

    return hresult;
}

/*++

CSCardWrapper::UnloadCOM

    This routine unloads the Vendor COM object which, in turn, unloads
    the low level COM API's

Arguments:

    none

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

--*/
void CSCardWrapper::UnloadCOM()
{
    //locals

    try {
        // UninitializeOLE if error has not occured
        if (m_sequence.COMLoad) {
            if (m_pISCardFileAccess != NULL)
                while (m_pISCardFileAccess->Release()>0);;
            if (m_pISCardVerify != NULL)
                while (m_pISCardVerify->Release()>0);
            if (m_pISCardAuth != NULL)
                while (m_pISCardAuth->Release()>0);
            if (m_pISCardManage != NULL)
                while (m_pISCardManage->Release()>0);
            CoUninitialize();
        };//if
    }

    catch(...) {
        TRACE_CATCH_UNKNOWN(_T("UnloadCOM"));
    }
}

/*++

CSCardWrapper::VerifyPIN

    This method does a PPIN verification with the card.

Arguments:

    wszPIN = a null terminated wide character string
    containing the PIN

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

--*/
HRESULT CSCardWrapper::VerifyPIN( WCHAR *wszPIN )
{
    //locals
    HRESULT     hresult = S_OK;
    BSTR        bstrPIN = NULL;

    try {
        // Are Attached?
        if (!m_sequence.Attach)
            throw ( (HRESULT) E_FAIL );
        if (wszPIN == NULL)
            throw ( (HRESULT) E_INVALIDARG );

        // Todo: convert to byte buffer and pass to vendor wrapper.

        //Ok...
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("VerifyPIN"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("VerifyPIN"));
    }

    // Free memory
    if (bstrPIN != NULL)
        ::SysFreeString(bstrPIN);

    return hresult;
}

/*++

CSCardWrapper::Write

    Write data to an open EF.

Arguments:

    hFile - handle to file.
    cbBytes - number of bytes to write
    pbyData - pointer to data to write.

Return Value:

    A HRESULT value indicating the status of the requested action.

    Return          Meaning
    ======          =======
    S_OK            Operation completed successfully.
    S_FALSE         Unknown error occurred.
    E_OUTOFMEMORY   There is not enough memory in this process to
                    satisfy the request.
    E_FAIL          Unspecified error.
    E_INVALIDARG    One or more arguments are invalid.
    E_UNEXPECTED    Unexpected event occurred in this function.

Author:

Notes:

--*/

HRESULT CSCardWrapper::Write(   HSCARD_FILE hFile,
                                BYTE byRecordID,
                                LONG cbBytes,
                                LPBYTE pbyData)
{
    //locals
    HRESULT     hresult = S_OK;
    LPBYTEBUFFER pBuffer = NULL;
    LARGE_INTEGER li;
    ULONG       ulBytesWritten = 0;

    try {
        // Are Attached?
        if (!m_sequence.Attach)
            throw ( (HRESULT) E_FAIL );
        if (pbyData == NULL)
            throw ( (HRESULT) E_INVALIDARG );
        if (cbBytes <0)
            throw ( (HRESULT) E_INVALIDARG );
        // Create ByteBuffer
        hresult = CreateByteBuffer( (DWORD) cbBytes,
                                    &pBuffer);
        if (FAILED(hresult))
            throw (hresult);
        // set seek position
        LISet32 (li, BYTE_BUFFER_HEAD );
        hresult = pBuffer->Seek((LONG) li.LowPart,
                                (LONG) STREAM_SEEK_SET,
                                NULL);
        if (FAILED(hresult))
            throw hresult;
        // Write data into return stream
        hresult = pBuffer->Write(   (BYTE*) pbyData,
                                    (LONG) cbBytes,
                                    (LONG*) &ulBytesWritten);
        if (FAILED(hresult))
            throw hresult;

        // Do the write...
        hresult = m_pISCardFileAccess->Write(   hFile,
ulBytesWritten,
                                                pBuffer,
                                                SC_FL_REPLACE);
        if (FAILED(hresult))
            throw (hresult);

        //Ok...
    }

    catch (HRESULT hr) {
        hresult = hr;
        TRACE_CATCH(_T("Write"), hr);
    }

    catch (...) {
        hresult = (HRESULT) E_UNEXPECTED;
        TRACE_CATCH_UNKNOWN(_T("Write"));
    }

    return hresult;
}


SCARDWRP.H

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

scardwrp

Abstract:

This file contains a simple class implementation for "wrapping"some
of the Vendor COM Wrapper API functionality.

Author:

Environment:

    Win32, C++ w/Exceptions, ATL, COM/OLE

Revision History:

--*/

#ifndef __SCARDWRP_H__
#define __SCARDWRP_H__

// These are required and taken from SCARDCOM.H
#ifndef _SCARD_STATES_DEFINED
#define _SCARD_STATES_DEFINED
typedef /* [version][v1_enum][helpstring] */
enum tagSCARD_STATES
    {ABSENT= 1,
PRESENT= 2,
SWALLOWED= 3,
POWERED= 4,
NEGOTIABLEMODE= 5,
SPECIFICMODE= 6
    }SCARD_STATES;

#endif // !_SCARD_STATES_DEFINED
#ifndef _SCARD_PROTOCOLS_DEFINED
#define _SCARD_PROTOCOLS_DEFINED
typedef /* [version][v1_enum][helpstring] */
enum tagSCARD_PROTOCOLS
    {T0= 0x1,
T1= 0x2,
RAW= 0xff
    }SCARD_PROTOCOLS;

#endif // !_SCARD_PROTOCOLS_DEFINED

#define BYTE_BUFFER_HEAD(0)

/////////////////////////////////////////////////////////////////////////////
//
// Includes
//

// Include Vendor COM object
#include "scardcom.h"

/////////////////////////////////////////////////////////////////////////////
// CSCardWrapper class

class CSCardWrapper
{
// Member Variables
private:
// Bit struct used to sequence events, etc.
typedef struct _SEQUENCE {
unsignedCOMLoad:1;
unsignedAttach:1;
unsignedOther:6;
} SEQUENCE;

SEQUENCEm_sequence;

// COM interface pointers
LPSCARDMANAGEm_pISCardManage;
LPSCARDVERIFYm_pISCardVerify;
LPSCARDAUTHm_pISCardAuth;
LPSCARDFILEACCESSm_pISCardFileAccess;

public:
// Construction/Destruction
CSCardWrapper() {m_pISCardManage = NULL;
m_pISCardVerify = NULL;
m_pISCardAuth = NULL;
m_pISCardFileAccess = NULL;

m_sequence.COMLoad = 0;
m_sequence.Attach = 0;
m_sequence.Other = 0;}

~CSCardWrapper() {}

private:
HRESULT CreateByteBuffer(DWORDdwSize,
LPBYTEBUFFER* ppBuffer);

public:
// Implementation
HRESULT AttachToReaderByName( WCHAR *wszReaderName );

HRESULT ChangeDirectory( WCHAR *wszDirectory );

HRESULT Detach();

HRESULT GetChallenge( LPBYTE lpbyChallenge, LONG* lpLengthOfChal );

HRESULT LoadCOM();

HRESULT Open( WCHAR *wszFilename, HSCARD_FILE *phFile );

HRESULT Status( SCARD_STATES *State, SCARD_PROTOCOLS *Protocol );

void UnloadCOM();

HRESULT VerifyPIN( WCHAR *wszPIN );

HRESULT Write ( HSCARD_FILE hFile, BYTE byRecordID,
LONG cbBytes, LPBYTE pbyData);
};

/////////////////////////////////////////////////////////////////////////////

#endif //__SCARDWRP_H__


SCARDVB.FRM

VERSION 5.00
Object = "{BDC217C8-ED16-11CD-956C-0000C04E4C0A}#1.1#0"; "TABCTL32.OCX"
Object = "{3B7C8863-D78F-101B-B9B5-04021C009402}#1.1#0"; "RICHTX32.OCX"
Begin VB.Form FrmSCardVB
   BorderStyle     =   1  'Fixed Single
   Caption         =   "SSP/Smart Card Test"
   ClientHeight    =   3945
   ClientLeft      =   3765
   ClientTop       =   3615
   ClientWidth     =   8625
   LinkTopic       =   "Form1"
   MaxButton       =   0   'False
   MinButton       =   0   'False
   PaletteMode     =   1  'UseZOrder
   ScaleHeight     =   3945
   ScaleWidth      =   8625
   Begin VB.TextBox ReaderName
      Height          =   285
      Left            =   3720
      TabIndex        =   9
      Text            =   "Bull CP8 Transac 0"
      Top             =   240
      Width           =   2775
   End
   Begin VB.CheckBox SaveToFile
      Caption         =   "Save results to file"
      Height          =   255
      Left            =   6840
      TabIndex        =   7
      Top             =   240
      Width           =   1695
   End
   Begin TabDlg.SSTab Tabs
      Height          =   3135
      Left            =   120
      TabIndex        =   4
      Top             =   720
      Width           =   7095
      _ExtentX        =   12515
      _ExtentY        =   5530
      _Version        =   327680
      Tabs            =   2
      TabHeight       =   520
      TabCaption(0)   =   "Test Step"
      TabPicture(0)   =   "scardvb.frx":0000
      Tab(0).ControlCount=   1
      Tab(0).ControlEnabled=   -1  'True
      Tab(0).Control(0)=   "Test_Step"
      Tab(0).Control(0).Enabled=   0   'False
      TabCaption(1)   =   "Test Output"
      Tab(1).ControlCount=   1
      Tab(1).ControlEnabled=   0   'False
      Tab(1).Control(0)=   "Test_Output"
      Tab(1).Control(0).Enabled=   0   'False
      Begin RichTextLib.RichTextBox Test_Step
         Height          =   2535
         Left            =   120
         TabIndex        =   6
         Top             =   480
         Width           =   6855
         _ExtentX        =   12091
         _ExtentY        =   4471
         _Version        =   327680
         ScrollBars      =   2
         TextRTF         =   $"scardvb.frx":001C
      End
      Begin RichTextLib.RichTextBox Test_Output
         Height          =   2535
         Left            =   -74880
         TabIndex        =   5
         Top             =   480
         Width           =   6855
         _ExtentX        =   12091
         _ExtentY        =   4471
         _Version        =   327680
         ScrollBars      =   2
         TextRTF         =   $"scardvb.frx":00ED
      End
   End
   Begin VB.CheckBox Log
      Caption         =   "Log results to file"
      Height          =   375
      Left            =   960
      TabIndex        =   3
      Top             =   840
      Width           =   1575
   End
   Begin VB.TextBox Iterations
      Alignment       =   1  'Right Justify
      Height          =   285
      Left            =   1680
      MaxLength       =   3
      TabIndex        =   2
      Text            =   "1"
      Top             =   240
      Width           =   495
   End
   Begin VB.CommandButton CallSSP
      Caption         =   "Run Test!"
      Height          =   495
      Left            =   7440
      TabIndex        =   0
      Top             =   3240
      Width           =   1095
   End
   Begin VB.Label Label2
      Alignment       =   1  'Right Justify
      Caption         =   "Reader Name:"
      Height          =   255
      Left            =   2520
      TabIndex        =   8
      Top             =   240
      Width           =   1095
   End
   Begin VB.Label Label1
      Caption         =   "Number of iterations:"
      Height          =   255
      Left            =   120
      TabIndex        =   1
      Top             =   240
      Width           =   1455
   End
End
Attribute VB_Name = "FrmSCardVB"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'
'
' Copyright (C) 1996-1997 Microsoft
'
' Module name:
'   SCardVB
'
' Abstract
'   This project is a simple example of how to use Visual Basic 5
'   with the smartcard low-level SSPs
'
'
' Notes:
'
'   1. The SCardDat, SCardMgr and SCardSrv type libraries must be referenced within this
'   project. (Use Project/References...)
'
'   2. This project expects a data file to be available on the card (ID: 3F05)
'   of type transparent EF, of size 120 bytes minimum
'
'   3. This is simply a brute force approach to show how VB can call the low-level
'   SSPs

'
' Set up variables for each Object
'
' Or, since the type library is referenced in this project use:
' Dim ISCardAuth as New CSCardAuth
'
Dim ISCardCmd As New CSCardCmd
Dim ISCardISO7816 As New CSCardISO7816
Dim ISCard As New CSCard

Private Sub CallSSP_Click()
On Error GoTo ErrorHandler
    '
    ' Catch the click and do something...
    '
    Dim lIterations As Long
    Dim lLength As Long
    Dim rgDataWritten(1) As Long
    Dim rgDataRead(1) As Long
    Dim TempBuffer(250) As Byte
    Dim Pattern_1(120) As Byte
    Dim Pattern_2(120) As Byte
    Dim bySW1, bySW2 As Byte
    Dim FileName As New ByteBuffer
    Dim Patterns As New ByteBuffer
    Dim Apdu As IByteBuffer
    Dim ApduReply As IByteBuffer
    Dim sDisplay As String
    Dim sDisplayStep As String
    Dim Reader As String

    ' Initialize Patterns
    For i = 0 To 14
        ' Pattern 1
        Pattern_1(i * 8 + 0) = 1: Pattern_1(i * 8 + 1) = 2
        Pattern_1(i * 8 + 2) = 3: Pattern_1(i * 8 + 3) = 4
        Pattern_1(i * 8 + 4) = 5: Pattern_1(i * 8 + 5) = 6
        Pattern_1(i * 8 + 6) = 7: Pattern_1(i * 8 + 7) = 8
        ' Pattern 2
        Pattern_2(i * 8 + 0) = 3: Pattern_2(i * 8 + 1) = 5
        Pattern_2(i * 8 + 2) = 3: Pattern_2(i * 8 + 3) = 5
        Pattern_2(i * 8 + 4) = 3: Pattern_2(i * 8 + 5) = 5
        Pattern_2(i * 8 + 6) = 3: Pattern_2(i * 8 + 7) = 5
    Next i

    ' Set readername
    Reader = ReaderName.Text

    ' Get number of iterations
    lIterations = Iterations.Text

    ' Attach to a card
    ISCard.AttachByReader Reader, Exclusive, T0

    ' Lock for transactions
    Call ISCard.LockSCard

    Call ISCardISO7816.SetDefaultClassId(192)

    Test_Output.Text = ""
    Test_Step.Text = ""

    For iter = 1 To lIterations
        ' Display Step
        Test_Output.Text = Test_Output.Text + "Iteration:" + CStr(iter) + Chr(10)
        Test_Step.Text = Test_Step.Text + "Iteration:" + CStr(iter) + Chr(10)

        ' Build Select MF Command
        TempBuffer(0) = 63: TempBuffer(1) = 0
        Call FileName.Initialize(2)
        Call FileName.Seek(0, 0)
        Call FileName.Write(TempBuffer(0), 2, rgDataWritten(0))
        Call ISCardISO7816.SelectFile(0, 0, FileName, 0, ISCardCmd)

        ' Select the MF
        Call ISCard.Transaction(ISCardCmd)
        bySW1 = ISCardCmd.ReplyStatusSW1
        bySW2 = ISCardCmd.ReplyStatusSW2
        sDisplayStep = Chr(10) + "SelectFile 3F00"

        ' Build Select Data file
        TempBuffer(0) = 63: TempBuffer(1) = 5
        Call FileName.Seek(0, 0)
        Call FileName.Write(TempBuffer(0), 2, rgDataWritten(0))
        Call ISCardISO7816.SelectFile(0, 0, FileName, 0, ISCardCmd)

        ' Select the Data file
        Call ISCard.Transaction(ISCardCmd)
        bySW1 = ISCardCmd.ReplyStatusSW1
        bySW2 = ISCardCmd.ReplyStatusSW2
        sDisplayStep = sDisplayStep + Chr(10) + "SelectFile 3F05"

        ' Write Data to file
        For i = 0 To 119
            TempBuffer(i) = Pattern_1(i)
        Next i

        Call Patterns.Initialize(120)
        Call Patterns.Seek(0, 0)
        Call Patterns.Write(TempBuffer(0), 120, rgDataWritten(0))
        Call ISCardISO7816.UpdateBinary(0, 0, Patterns, ISCardCmd)

        ' Write Data
        Call ISCard.Transaction(ISCardCmd)
        bySW1 = ISCardCmd.ReplyStatusSW1
        bySW2 = ISCardCmd.ReplyStatusSW2
        sDisplayStep = sDisplayStep + Chr(10) + "UpdateBinary Pattern 1"

        ' Read Pattern back
        Call ISCardISO7816.ReadBinary(0, 0, 120, ISCardCmd)

        ' Read Data
        Call ISCard.Transaction(ISCardCmd)
        bySW1 = ISCardCmd.ReplyStatusSW1
        bySW2 = ISCardCmd.ReplyStatusSW2
        sDisplayStep = sDisplayStep + Chr(10) + "ReadBinary Pattern 1"

        lLength = ISCardCmd.ApduReplyLength
        Set ApduReply = ISCardCmd.ApduReply

        Call ApduReply.Read(TempBuffer(0), lLength, rgDataRead(0))
        sText = Chr(10) + "Pattern 1:" + Chr(10)
        For i = 0 To lLength / 16
            sDisplay = CStr(TempBuffer(i * 8 + 0)) + " " + CStr(TempBuffer(i * 8 + 1)) + " " + CStr(TempBuffer(i * 8 + 2)) + " " + CStr(TempBuffer(i * 8 + 3)) + " " + CStr(TempBuffer(i * 8 + 4)) + " " + CStr(TempBuffer(i * 8 + 5)) + " " + CStr(TempBuffer(i * 8 + 6)) + " " + CStr(TempBuffer(i * 8 + 7)) + " "
            sdisplay2 = CStr(TempBuffer(i * 8 + 8)) + " " + CStr(TempBuffer(i * 8 + 9)) + " " + CStr(TempBuffer(i * 8 + 10)) + " " + CStr(TempBuffer(i * 8 + 11)) + " " + CStr(TempBuffer(i * 8 + 12)) + " " + CStr(TempBuffer(i * 8 + 13)) + " " + CStr(TempBuffer(i * 8 + 14)) + " " + CStr(TempBuffer(i * 8 + 15)) + " "
            sText = sText + sDisplay + sdisplay2 + Chr(10)
        Next i

        ' Write Data to file
        For i = 0 To 119
            TempBuffer(i) = Pattern_2(i)
        Next i

        Call Patterns.Seek(0, 0)
        Call Patterns.Write(TempBuffer(0), 120, rgDataWritten(0))
        Call ISCardISO7816.UpdateBinary(0, 0, Patterns, ISCardCmd)

        ' Write Data
        Call ISCard.Transaction(ISCardCmd)
        bySW1 = ISCardCmd.ReplyStatusSW1
        bySW2 = ISCardCmd.ReplyStatusSW2
        sDisplayStep = sDisplayStep + Chr(10) + "UpdateBinary Pattern 2"

        ' Read Pattern back
        Call ISCardISO7816.ReadBinary(0, 0, 120, ISCardCmd)

        ' Read Data
        Call ISCard.Transaction(ISCardCmd)
        bySW1 = ISCardCmd.ReplyStatusSW1
        bySW2 = ISCardCmd.ReplyStatusSW2
        sDisplayStep = sDisplayStep + Chr(10) + "ReadBinary Pattern 2" + Chr(10)

        lLength = ISCardCmd.ApduReplyLength
        Set ApduReply = ISCardCmd.ApduReply

        ' Build buffer for text display
        Call ApduReply.Read(TempBuffer(0), lLength, rgDataRead(0))
        sText = sText + Chr(10) + "Pattern 2:" + Chr(10)
        For i = 0 To lLength / 16
            sDisplay = CStr(TempBuffer(i * 8 + 0)) + " " + CStr(TempBuffer(i * 8 + 1)) + " " + CStr(TempBuffer(i * 8 + 2)) + " " + CStr(TempBuffer(i * 8 + 3)) + " " + CStr(TempBuffer(i * 8 + 4)) + " " + CStr(TempBuffer(i * 8 + 5)) + " " + CStr(TempBuffer(i * 8 + 6)) + " " + CStr(TempBuffer(i * 8 + 7)) + " "
            sdisplay2 = CStr(TempBuffer(i * 8 + 8)) + " " + CStr(TempBuffer(i * 8 + 9)) + " " + CStr(TempBuffer(i * 8 + 10)) + " " + CStr(TempBuffer(i * 8 + 11)) + " " + CStr(TempBuffer(i * 8 + 12)) + " " + CStr(TempBuffer(i * 8 + 13)) + " " + CStr(TempBuffer(i * 8 + 14)) + " " + CStr(TempBuffer(i * 8 + 15)) + " "
            sText = sText + sDisplay + sdisplay2 + Chr(10)
        Next i

        Test_Output.Text = Test_Output.Text + sText + Chr(10)
        Test_Step.Text = Test_Step.Text + sDisplayStep + Chr(10)

    Next iter

    ' Unlock
    Call ISCard.UnlockSCard(LEAVE)

    ' Detach from card
    ISCard.Detach (1)

    '
    ' Unload the objects
    '
    Set ISCard = Nothing
    Set ISCardISO7816 = Nothing
    Set ISCardCmd = Nothing

    ' Leave
    Exit Sub

ErrorHandler:
    '
    ' Unload the objects
    '
    Set ISCard = Nothing
    Set ISCardISO7816 = Nothing
    Set ISCardCmd = Nothing

End Sub

Private Sub Form_Load()
On Error GoTo ErrorHandler
    '
    ' Center the dialog
    '
    Left = (Screen.Width - Width) / 2
    Top = (Screen.Height - Height) / 2

    Test_Output.Text = ""
    Test_Step.Text = ""

    ' Leave
    Exit Sub

ErrorHandler:
    ' Todo: Add error handling code

End Sub

Private Sub Form_Terminate()
On Error GoTo ErrorHandler
    '
    ' Unload the objects
    '
    Set ISCard = Nothing
    Set ISCardTypeConv = Nothing
    Set ISCardCmd = Nothing

    ' Clear text boxes
    Test_Output.Text = " "
    Test_Step.Text = " "

    ' Leave
    Exit Sub

ErrorHandler:
    ' Todo: Add error handling code

End Sub

Private Sub Form_Unload(Cancel As Integer)
On Error GoTo ErrorHandler
    '
    ' Unload the objects
    '
    Set ISCard = Nothing
    Set ISCardTypeConv = Nothing
    Set ISCardCmd = Nothing

    ' Save data if needed
    If SaveToFile.Value = 1 Then
        Call Test_Output.SaveFile("SSPTestDat_1.rtf", 0)
        Call Test_Step.SaveFile("SSPTestDat_2.rtf", 0)
    End If

    ' Leave
    Exit Sub

ErrorHandler:
    ' Todo: Add error handling code

End Sub


README.TXT

The source code in this directory can be used to build a sample Exit Module
for the Microsoft Certificate Server.  It is meant to run on Windows NT 4.0
with SP3 or later or on Windows NT 5.0 only.  The Certificate Server must
already be installed.

The Certificate Server calls the Exit Module through the ICertExit interface,
and the Exit Module can call back to the Certificate Server through the
ICertServerExit interface.

Each time the Certificate Server issues a certificate, it passes control to
the CCertExit::Notify method in exit.cpp, specifying that a certificate
has been issued.  The passed Context parameter is used with the ICertServerExit
interface to retrieve properties from the newly issued certificate.

Once the certexit.dll DLL is built, its COM interface must be registered
via the following command:
    regsvr32 certexit.dll
and the Certificate Server service must be stopped and restarted to load
the newly registered Exit Module.  Use the Control Panel's Services applet,
and stop and restart the "Certificate Authority" service.

If you wish to see the printf output from the sample Exit Module, register
the Exit Module and stop the Certificate Server service as above, then start
the Certificate Server as a console application via the following
command:
    certsrv -z

Files:
------
atl.cpp      -- ActiveX Template Library COM support code

certexit.cpp -- Implements COM and initialization entry points:
DllMain
DllCanUnloadNow
DllGetClassObject
DllRegisterServer
DllUnregisterServer

certexit.def -- Exports COM entry points

certexit.rc  -- Version Resource

exit.cpp     -- Implements ICertExit

exit.h       -- Implements ICertExit

pch.cpp      -- Precompiled Header file

resource.h   -- Resource ID definitions


CERTEXIT.CPP

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996-1997
//
// File:        certcli.cpp
//
// Contents:    Cert Server Exit Module implementation
//
//---------------------------------------------------------------------------

#include "pch.cpp"

#pragma hdrstop

#include "exit.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
    OBJECT_ENTRY(CLSID_CCertExit, CCertExit)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
    switch (dwReason)
    {
case DLL_PROCESS_ATTACH:
    _Module.Init(ObjectMap, hInstance);
    DisableThreadLibraryCalls(hInstance);
    break;

        case DLL_PROCESS_DETACH:
    _Module.Term();
            break;
    }
    return(TRUE);    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI
DllCanUnloadNow(void)
{
    return(_Module.GetLockCount() == 0? S_OK : S_FALSE);
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI
DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    return(_Module.GetClassObject(rclsid, riid, ppv));
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI
DllRegisterServer(void)
{
    // registers object, typelib and all interfaces in typelib
    return(_Module.RegisterServer(TRUE));
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI
DllUnregisterServer(void)
{
    _Module.UnregisterServer();
    return(S_OK);
}


CERTEXIT.IDL

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996-1996
//
// File:        certexit.idl
//
// Contents:    IDL source for certexit.dll
//
//---------------------------------------------------------------------------

// This file will be processed by the MIDL tool to produce the type library
// (certexit.tlb) and marshalling code.

import "wtypes.idl";

const LONG EXITEVENT_INVALID =       0x00000000;// invalid event
const LONG EXITEVENT_CERTISSUED =   0x00000001;// Certificate issued
const LONG EXITEVENT_CERTPENDING =   0x00000002;// Certificate pending
const LONG EXITEVENT_CERTDENIED =    0x00000004;// Certificate denied
const LONG EXITEVENT_CERTREVOKED =    0x00000008;// Certificate revoked
const LONG EXITEVENT_CERTRETRIEVEPENDING = 0x00000010;// Certificate retrieval
const LONG EXITEVENT_CRLISSUED =   0x00000020;// CRL issued
const LONG EXITEVENT_SHUTDOWN =   0x00000040;// Server shutdown

//+--------------------------------------------------------------------------
// ICertExit class -- local COM interface, local implementation
//+--------------------------------------------------------------------------

[
    object,
    uuid(e19ae1a0-7364-11d0-8816-00a0c903b83c),
    dual,
    helpstring("ICertExit Interface"),
    pointer_default(unique)
]

interface ICertExit: IDispatch
{
    import "oaidl.idl";

    HRESULT Initialize(
[in] BSTR const strConfig,
[out, retval] LONG *pEventMask);

    HRESULT Notify(
[in] LONG Event,
[in] LONG Context);

    HRESULT GetDescription(
[out, retval] BSTR *pstrDescription);
};

//+--------------------------------------------------------------------------
// certexit Type library
//+--------------------------------------------------------------------------

[
    uuid(e1f81810-7364-11d0-8816-00a0c903b83c),
    version(1.0),
    helpstring("CertExit 1.0 Type Library")
]

library CERTEXITLib
{
    importlib("stdole2.tlb");

    [
uuid(c9e6e410-7364-11d0-8816-00a0c903b83c),
helpstring("CertExit Class")
    ]
    coclass CCertExit
    {
[default] interface ICertExit;
    };
};


EXIT.CPP

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996-1997
//
// File:        exit.cpp
//
// Contents:    CCertExit implementation
//
//---------------------------------------------------------------------------

#include "pch.cpp"
#pragma hdrstop

#include <stdio.h>
#include "exit.h"

#define myEXITEVENTS \
EXITEVENT_CERTISSUED | \
EXITEVENT_CERTPENDING | \
EXITEVENT_CERTDENIED | \
EXITEVENT_CERTREVOKED | \
EXITEVENT_CERTRETRIEVEPENDING | \
EXITEVENT_CRLISSUED | \
EXITEVENT_SHUTDOWN

#ifndef DBG_CERTSRV
#error -- DBG_CERTSRV not defined!
#endif
BOOL fDebug = DBG_CERTSRV;

#define wszDESCRIPTIONL"Sample Exit Module"

//+--------------------------------------------------------------------------
// CCertExit::~CCertExit -- destructor
//
// free memory associated with this instance
//+--------------------------------------------------------------------------

CCertExit::~CCertExit()
{
    if (NULL != m_strConfig)
    {
SysFreeString(m_strConfig);
    }
}

//+--------------------------------------------------------------------------
// CCertExit::Initialize -- initialize for a CA & return interesting Event Mask
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

STDMETHODIMP
CCertExit::Initialize(
    /* [in] */ BSTR const strConfig,
    /* [retval][out] */ LONG __RPC_FAR *pEventMask)
{
    HRESULT hr = S_OK;

    m_strConfig = SysAllocString(strConfig);
    if (NULL == m_strConfig)
    {
hr = E_OUTOFMEMORY;
    }
    else
    {
*pEventMask = myEXITEVENTS;
printf("Exit::Initialize(%ws) ==> %x\n", m_strConfig, *pEventMask);
    }
    return(hr);
}

HRESULT
EnumerateExtensions(
    IN ICertServerExit *pServer)
{
    HRESULT hr;
    HRESULT hr2;
    BSTR strName = NULL;
    LONG ExtFlags;
    VARIANT varValue;
    BOOL fClose = FALSE;

    VariantInit(&varValue);
    hr = pServer->EnumerateExtensionsSetup(0);
    if (S_OK != hr)
    {
if (fDebug)
{
    printf("Exit:EnumerateExtensionsSetup: %x", hr);
}
goto error;
    }
    fClose = TRUE;
    while (TRUE)
    {
hr = pServer->EnumerateExtensions(&strName);
if (S_OK != hr)
{
    if (S_FALSE == hr)
    {
hr = S_OK;
break;
    }
    if (fDebug)
    {
printf("Exit:EnumerateExtensions: %x", hr);
    }
    goto error;
}
hr = pServer->GetCertificateExtension(
strName,
PROPTYPE_BINARY,
&varValue);
if (S_OK != hr)
{
    if (fDebug)
    {
printf("Exit:GetCertificateExtension: %x", hr);
    }
    goto error;
}
hr = pServer->GetCertificateExtensionFlags(&ExtFlags);
if (S_OK != hr)
{
    if (fDebug)
    {
printf("Exit:GetCertificateExtensionFlags: %x", hr);
    }
    goto error;
}
if (fDebug)
{
    printf(
"Exit:EnumerateExtensions(%ws, Flags=%x, %x bytes)\n",
strName,
ExtFlags,
SysStringByteLen(varValue.bstrVal));
}
VariantClear(&varValue);
    }

error:
    if (fClose)
    {
hr2 = pServer->EnumerateExtensionsClose();
if (S_OK != hr2)
{
    if (fDebug)
    {
printf("Exit:EnumerateExtensionsClose: %x", hr2);
    }
    if (S_OK == hr)
    {
hr = hr2;
    }
    goto error;
}
    }
    if (NULL != strName)
    {
SysFreeString(strName);
    }
    VariantClear(&varValue);
    return(hr);
}

HRESULT
EnumerateAttributes(
    IN ICertServerExit *pServer)
{
    HRESULT hr;
    HRESULT hr2;
    BSTR strName = NULL;
    BOOL fClose = FALSE;
    BSTR strValue = NULL;

    hr = pServer->EnumerateAttributesSetup(0);
    if (S_OK != hr)
    {
if (fDebug)
{
    printf("Exit:EnumerateAttributesSetup: %x", hr);
}
goto error;
    }
    fClose = TRUE;
    while (TRUE)
    {
hr = pServer->EnumerateAttributes(&strName);
if (S_OK != hr)
{
    if (S_FALSE == hr)
    {
hr = S_OK;
break;
    }
    if (fDebug)
    {
printf("Exit:EnumerateAttributes: %x", hr);
    }
    goto error;
}
hr = pServer->GetRequestAttribute(strName, &strValue);
if (S_OK != hr)
{
    if (fDebug)
    {
printf("Exit:GetRequestAttribute: %x", hr);
    }
    goto error;
}
if (fDebug)
{
    printf("Exit:EnumerateAttributes(%ws = %ws)\n", strName, strValue);
}
if (NULL != strValue)
{
    SysFreeString(strValue);
    strValue = NULL;
}
    }

error:
    if (fClose)
    {
hr2 = pServer->EnumerateAttributesClose();
if (S_OK != hr2)
{
    if (fDebug)
    {
printf("Exit:EnumerateAttributesClose: %x", hr2);
    }
    if (S_OK == hr)
    {
hr = hr2;
    }
    goto error;
}
    }

    if (NULL != strName)
    {
SysFreeString(strName);
    }
    if (NULL != strValue)
    {
SysFreeString(strValue);
    }
    return(hr);
}

HRESULT
CheckRequestProperties(
    IN ICertServerExit *pServer)
{
    HRESULT hr;
    VARIANT varValue;
    BSTR strName = NULL;

    VariantInit(&varValue);

    strName = SysAllocString(wszPROPREQUESTREQUESTID);
    if (NULL == strName)
    {
hr = E_OUTOFMEMORY;
goto error;
    }

    hr = pServer->GetRequestProperty(strName, PROPTYPE_LONG, &varValue);
    if (S_OK != hr)
    {
if (fDebug)
{
    printf("Exit:GetRequestProperty: %x", hr);
}
goto error;
    }
    if (fDebug)
    {
printf(
    "Exit:CheckRequestProperties(%ws = %x)\n",
    strName,
    varValue.lVal);
    }
    VariantClear(&varValue);

error:
    if (NULL != strName)
    {
SysFreeString(strName);
    }
    return(hr);
}

HRESULT
CheckCert(
    LONG Context)
{
    HRESULT hr;
    ICertServerExit *pServer = NULL;

    hr = CoCreateInstance(
    CLSID_CCertServerExit,
    NULL,// pUnkOuter
    CLSCTX_INPROC_SERVER,
    IID_ICertServerExit,
    (VOID **) &pServer);
    if (S_OK != hr)
    {
goto exit;
    }

    hr = pServer->SetContext(Context);
    if (S_OK != hr)
    {
goto exit;
    }
    hr = EnumerateExtensions(pServer);
    if (S_OK != hr)
    {
goto exit;
    }

    hr = EnumerateAttributes(pServer);
    if (S_OK != hr)
    {
goto exit;
    }

    hr = CheckRequestProperties(pServer);
    if (S_OK != hr)
    {
goto exit;
    }

exit:
    if (NULL != pServer)
    {
pServer->Release();
    }
    return(hr);
}

//+--------------------------------------------------------------------------
// CCertExit::Notify -- Notify the exit module of an event
//
// Returns S_OK.
//+--------------------------------------------------------------------------

STDMETHODIMP
CCertExit::Notify(
    /* [in] */ LONG Event,
    /* [in] */ LONG Context)
{
    HRESULT hr = S_OK;
    char *psz = "UNKNOWN EVENT";

    switch (Event)
    {
case EXITEVENT_CERTISSUED:
    psz = "certissued";
    break;

case EXITEVENT_CERTPENDING:
    psz = "certpending";
    break;

case EXITEVENT_CERTDENIED:
    psz = "certdenied";
    break;

case EXITEVENT_CERTREVOKED:
    psz = "certrevoked";
    break;

case EXITEVENT_CERTRETRIEVEPENDING:
    psz = "retrievepending";
    break;

case EXITEVENT_CRLISSUED:
    psz = "crlissued";
    break;

case EXITEVENT_SHUTDOWN:
    psz = "shutdown";
    break;
    }
    printf("Exit::Notify(%s=%x, ctx=%u)\n", psz, Event, Context);

    if (EXITEVENT_CERTISSUED == Event)
    {
hr = CheckCert(Context);
    }
    return(hr);
}

STDMETHODIMP
CCertExit::GetDescription(
    /* [retval][out] */ BSTR *pstrDescription)
{
    HRESULT hr = S_OK;

    *pstrDescription = SysAllocString(wszDESCRIPTION);
    if (NULL == *pstrDescription)
    {
hr = E_OUTOFMEMORY;
    }
    return(hr);
}

/////////////////////////////////////////////////////////////////////////////
//

STDMETHODIMP
CCertExit::InterfaceSupportsErrorInfo(REFIID riid)
{
    int i;
    static const IID *arr[] =
    {
&IID_ICertExit,
    };

    for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
    {
if (InlineIsEqualGUID(*arr[i],riid))
{
    return(S_OK);
}
    }
    return(S_FALSE);
}


EXIT.H

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996-1997
//
// File:        exit.cpp
//
// Contents:    CCertExit definition
//
//---------------------------------------------------------------------------

#include "resource.h"       // main symbols
#include <certexit.h>

/////////////////////////////////////////////////////////////////////////////
// certexit

class CCertExit:
    public CComDualImpl<ICertExit, &IID_ICertExit, &LIBID_CERTEXITLib>,
    public ISupportErrorInfo,
    public CComObjectRoot,
    public CComCoClass<CCertExit, &CLSID_CCertExit>
{
public:
    CCertExit() { m_strConfig = NULL; }
    ~CCertExit();

BEGIN_COM_MAP(CCertExit)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ICertExit)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()

DECLARE_NOT_AGGREGATABLE(CCertExit)

DECLARE_REGISTRY(
    CCertExit,
    wszCLASS_CERTEXIT TEXT(".1"),
    wszCLASS_CERTEXIT,
    IDS_CERTEXIT_DESC,
    THREADFLAGS_BOTH)

    // ISupportsErrorInfo
    STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

    // ICertExit
public:
    STDMETHOD(Initialize)(
            /* [in] */ BSTR const strConfig,
            /* [retval][out] */ LONG __RPC_FAR *pEventMask);

    STDMETHOD(Notify)(
            /* [in] */ LONG Event,
            /* [in] */ LONG Context);

    STDMETHOD(GetDescription)(
            /* [retval][out] */ BSTR *pstrDescription);

private:
    // Member variables & private methods here:
    BSTR m_strConfig;
};


PCH.CPP

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996-1997
//
// File:        pch.cpp
//
// Contents:    Cert Server precompiled header
//
//---------------------------------------------------------------------------

#include <windows.h>

#include <atlbase.h>

//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;

#include <atlcom.h>
#include <certsrv.h>

#pragma hdrstop


ATL.CPP

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996-1997
//
// File:        atl.cpp
//
// Contents:    ActiveX Template Library COM support
//
//---------------------------------------------------------------------------

#include <pch.cpp>

#pragma hdrstop

#include <atlimpl.cpp>


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by certexit.rc
//
#define IDS_CERTEXIT_DESC1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

The source code in this directory can be used to build a sample Policy Module
for the Microsoft Certificate Server.  It is meant to run on Windows NT 4.0
with SP3 or later or on Windows NT 5.0 only.  The Certificate Server must
already be installed.

The Certificate Server calls the Policy Module through the ICertPolicy
interface, and the Policy Module can call back to the Certificate Server
through the ICertServerPolicy interface.

Each time the Certificate Server receives a certificate request, it passes
control to the CCertPolicy::VerifyRequest method in policy.cpp.  The passed
Context parameter is used with the ICertServerPolicy interface to retrieve
properties from the request and potential certificate.  The VerifyRequest
method may add, modify or enable Certificate Extensions, modify the NotBefore
and NotAfter dates and Subject name RDN (Relative Distinguished Name) strings
for the potential certificate.  It must also perform any validation required,
and decide the disposition of the request.  The method should return one of
VR_PENDING, VR_INSTANT_OK or VR_INSTANT_BAD to cause the request to be made
pending, to grant the request and issue the certificate, or to fail the
request.

Once the certpdef.dll DLL is built, its COM interface must be registered
via the following command:
    regsvr32 certpdef.dll
and the Certificate Server service must be stopped and restarted to load
the newly registered Policy Module.  Use the Control Panel's Services applet,
and stop and restart the "Certificate Authority" service.

If you wish to see the printf output from the sample Policy Module, register
the Policy Module and stop the Certificate Server service as above, then start
the Certificate Server as a console application via the following
command:
    certsrv -z

Files:
------
atl.cpp      -- ActiveX Template Library COM support code

certpdef.cpp -- Implements COM and initialization entry points:
DllMain
DllCanUnloadNow
DllGetClassObject
DllRegisterServer
DllUnregisterServer

certpdef.def -- Exports COM entry points

certpdef.rc  -- Version Resource

lib.cpp      -- Implements a routine to convert a string to BSTR

lib.h        -- Prototype for routine to convert a string to BSTR

pch.cpp      -- Precompiled Header file

policy.cpp   -- Implements ICertPolicy

policy.h     -- Implements ICertPolicy

resource.h   -- Resource ID definitions


CERTPDEF.CPP

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996-1997
//
// File:        certpdef.cpp
//
// Contents:    Cert Server Policy Module implementation
//
//---------------------------------------------------------------------------

#include "pch.cpp"

#pragma hdrstop

#include "resource.h"
#include "policy.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
    OBJECT_ENTRY(CLSID_CCertPolicy, CCertPolicy)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
    switch (dwReason)
    {
case DLL_PROCESS_ATTACH:
    _Module.Init(ObjectMap, hInstance);
    DisableThreadLibraryCalls(hInstance);
    break;

        case DLL_PROCESS_DETACH:
    _Module.Term();
            break;
    }
    return(TRUE);    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI
DllCanUnloadNow(void)
{
    return(_Module.GetLockCount() == 0? S_OK : S_FALSE);
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI
DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    return(_Module.GetClassObject(rclsid, riid, ppv));
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI
DllRegisterServer(void)
{
    // registers object, typelib and all interfaces in typelib
    return(_Module.RegisterServer(TRUE));
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI
DllUnregisterServer(void)
{
    _Module.UnregisterServer();
    return(S_OK);
}

void __RPC_FAR *__RPC_USER
MIDL_user_allocate(size_t cb)
{
    return(LocalAlloc(LMEM_ZEROINIT, cb));
}

void __RPC_USER
MIDL_user_free(void __RPC_FAR *pb)
{
    LocalFree(pb);
}


CERTPOL.IDL

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996-1996
//
// File:        certpol.idl
//
// Contents:    IDL source for certpol.dll
//
//---------------------------------------------------------------------------

// This file will be processed by the MIDL tool to produce the type library
// (certpol.tlb) and marshalling code.

import "wtypes.idl";

//+--------------------------------------------------------------------------
// ICertPolicy class -- local COM interface
//+--------------------------------------------------------------------------

[
    object,
    uuid(38bb5a00-7636-11d0-b413-00a0c91bbf8c),
    dual,
    helpstring("ICertPolicy Interface"),
    pointer_default(unique)
]

interface ICertPolicy: IDispatch
{
    import "oaidl.idl";

    HRESULT Initialize(
[in] BSTR const strConfig);

    HRESULT VerifyRequest(
[in] BSTR const strConfig,
[in] LONG Context,
[in] LONG bNewRequest,
[in] LONG Flags,
[out, retval] LONG *pDisposition);

    HRESULT GetDescription(
[out, retval] BSTR *pstrDescription);

    HRESULT ShutDown();
};

//+--------------------------------------------------------------------------
// certpol Type library
//+--------------------------------------------------------------------------

[
    uuid(39c0a710-7636-11d0-b413-00a0c91bbf8c),
    version(1.0),
    helpstring("CertPol 1.0 Type Library")
]

library CERTPOLICYLib
{
    importlib("stdole2.tlb");

    [
uuid(3a428a00-7636-11d0-b413-00a0c91bbf8c),
helpstring("CertPolicy Class")
    ]
    coclass CCertPolicy
    {
[default] interface ICertPolicy;
    };
};


LIB.CPP

//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1995 - 1997
//
//  File:       lib.cpp
//
//  Contents:   helper functions
//
//--------------------------------------------------------------------------

#include "pch.cpp"
#pragma hdrstop

#include "lib.h"

BOOL
ConvertWszToBstr(
    OUT BSTR *pbstr,
    IN WCHAR const *pwc,
    IN LONG cb)
{
    BOOL fOk = FALSE;
    BSTR bstr;

    do
    {
bstr = NULL;
if (NULL != pwc)
{
    if (-1 == cb)
    {
cb = wcslen(pwc) * sizeof(WCHAR);
    }
    bstr = SysAllocStringByteLen((char const *) pwc, cb);
    if (NULL == bstr)
    {
break;
    }
}
if (NULL != *pbstr)
{
    SysFreeString(*pbstr);
}
*pbstr = bstr;
fOk = TRUE;
    } while (FALSE);
    return(fOk);
}


LIB.H

//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1995 - 1997
//
//  File:       lib.h
//
//  Contents:   helper functions
//
//--------------------------------------------------------------------------

#ifndef __LIB_H__
#define __LIB_H__

BOOL
ConvertWszToBstr(
    OUT BSTR *pbstr,
    IN WCHAR const *pwc,
    IN LONG cb);

#endif // __LIB_H__


POLICY.CPP

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright 1996 - 1998 Microsoft Corporation-1997
//
// File:        policy.cpp
//
// Contents:    Cert Server Policy Module implementation
//
//---------------------------------------------------------------------------
#include "pch.cpp"
#pragma hdrstop

#include "lib.h"
#include "policy.h"
#include <stdio.h>
#include <assert.h>

#ifndef DBG_CERTSRV
#error -- DBG_CERTSRV not defined!
#endif

BOOL fDebug = DBG_CERTSRV;

#define wszDESCRIPTION  L"Microsoft Certificate Server Default Policy Module"

//+--------------------------------------------------------------------------
// CCertPolicy::~CCertPolicy -- destructor
//
// free memory associated with this instance
//+--------------------------------------------------------------------------

CCertPolicy::~CCertPolicy()
{
    _Cleanup();
}

//+--------------------------------------------------------------------------
// CCertPolicy::_Cleanup -- free memory associated with this instance
//
// free memory associated with this instance
//+--------------------------------------------------------------------------

VOID
CCertPolicy::_Cleanup()
{
    DWORD i;

    // RevocationExtension variables:

    if (NULL != m_apstrRevocationURL)
    {
        for (i = 0; i < m_cRevocationURL; i++)
        {
            if (NULL != m_apstrRevocationURL[i])
            {
                SysFreeString(m_apstrRevocationURL[i]);
            }
        }
        LocalFree(m_apstrRevocationURL);
        m_apstrRevocationURL = NULL;
    }

    // CertTypeExtension variables:

    m_RevocationType = REVTYPE_NONE;

    // SubjectAltNameExtension variables:

    for (i = 0; i < 2; i++)
    {
if (NULL != m_astrSubjectAltNameProp[i])
{
    SysFreeString(m_astrSubjectAltNameProp[i]);
    m_astrSubjectAltNameProp[i] = NULL;
}
if (NULL != m_astrSubjectAltNameObjectId[i])
{
    SysFreeString(m_astrSubjectAltNameObjectId[i]);
    m_astrSubjectAltNameObjectId[i] = NULL;
}
    }
}

//+--------------------------------------------------------------------------
// CCertPolicy::_InitRevocationExtension --
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

VOID
CCertPolicy::_InitRevocationExtension(
    IN HKEY hkey)
{
    DWORD err;
    DWORD i;
    DWORD dwType;
    DWORD cbbuf;
    WCHAR *pwc;
    WCHAR const *pwszRegRevocation;
    WCHAR awcbuf[MAX_PATH];

    cbbuf = sizeof(awcbuf);
    err = RegQueryValueEx(
                hkey,
                wszREGREVOCATIONTYPE,
                NULL,           // lpdwReserved
                &dwType,
                (BYTE *) awcbuf,
                &cbbuf);
    if (ERROR_SUCCESS != err || REG_SZ != dwType || sizeof(awcbuf) <= cbbuf)
    {
        goto exit;
    }
    if (0 == lstrcmpi(awcbuf, wszREVTYPE_CRL))
    {
        m_RevocationType = REVTYPE_CRLDIST;
        pwszRegRevocation = wszREGREVOCATIONCRLURL;
    }
    else
    if (0 == lstrcmpi(awcbuf, wszREVTYPE_NETSCAPE))
    {
        m_RevocationType = REVTYPE_NETSCAPE;
        pwszRegRevocation = wszREGREVOCATIONURL;
    }
    else
    {
        goto exit;
    }

    cbbuf = sizeof(awcbuf) - 2 * sizeof(WCHAR);
    err = RegQueryValueEx(
    hkey,
    pwszRegRevocation,
    NULL,           // lpdwReserved
    &dwType,
    (BYTE *) awcbuf,
    &cbbuf);
    if (ERROR_SUCCESS != err ||
        (REG_SZ != dwType && REG_MULTI_SZ != dwType) ||
        sizeof(awcbuf) - 2 * sizeof(WCHAR) <= cbbuf)
    {
        goto exit;
    }

    // Double null terminate to make a REG_SZ value look like REG_MULTI_SZ.

    awcbuf[cbbuf/sizeof(WCHAR)] = L'\0';
    awcbuf[cbbuf/sizeof(WCHAR) + 1] = L'\0';

    m_cRevocationURL = 0;
    pwc = awcbuf;
    while (TRUE)
    {
        pwc = wcschr(pwc, L'\0');
        assert(NULL != pwc);
        pwc++;
        m_cRevocationURL++;
        if (L'\0' == *pwc)
        {
            break;
        }
    }

    m_apstrRevocationURL = (BSTR *) LocalAlloc(
                                        LMEM_FIXED,
                                        m_cRevocationURL * sizeof(BSTR));
    if (NULL == m_apstrRevocationURL)
    {
        goto exit;
    }
    memset(m_apstrRevocationURL, 0, m_cRevocationURL * sizeof(BSTR));

    pwc = awcbuf;
    for (i = 0; i < m_cRevocationURL; i++)
    {
        DWORD cwc = wcslen(SysAllocString(pwc));

        if (REVTYPE_NETSCAPE == m_RevocationType)
        {
            cwc++;
        }
        m_apstrRevocationURL[i] = SysAllocStringLen(NULL, cwc);
        if (NULL == m_apstrRevocationURL[i])
        {
            _Cleanup();
            goto exit;
        }
        wcscpy(m_apstrRevocationURL[i], pwc);
        if (REVTYPE_NETSCAPE == m_RevocationType)
        {
            wcscat(m_apstrRevocationURL[i], L"?");
        }
        if (fDebug)
        {
            printf(
                "Policy: Revocation URL(type=%u): %s[%u]: %ws\n",
                m_RevocationType,
                REVTYPE_CRLDIST == m_RevocationType?
                    "CRLDistPoint" : "Netscape",
                i,
                m_apstrRevocationURL[i]);
        }
        pwc = wcschr(pwc, L'\0');
        assert(NULL != pwc);
        pwc++;
        assert(L'\0' != *pwc || i + 1 == m_cRevocationURL);
    }

exit:
    ;
}

//+--------------------------------------------------------------------------
// CCertPolicy::_InitCertTypeExtension --
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

VOID
CCertPolicy::_InitCertTypeExtension(
    IN HKEY hkey)
{
    DWORD err;
    DWORD dwType;
    DWORD cbbuf;
    DWORD dwCertType;

    cbbuf = sizeof(dwCertType);
    err = RegQueryValueEx(
                          hkey,
                          wszREGNETSCAPECERTTYPE,
                          NULL,         // lpdwReserved
                          &dwType,
                          (BYTE *) &dwCertType,
                          &cbbuf);
    if (ERROR_SUCCESS != err || REG_DWORD != dwType)
    {
        goto exit;
    }
    if (dwCertType == CERTTYPE_NETSCAPE)
    {
        m_CertType = CERTTYPE_NETSCAPE;
    }
    if (fDebug && m_CertType)
    {
        printf(
            "Policy: Netscape Certificate Type(type=%u): %s\n",
            m_CertType,
            CERTTYPE_NETSCAPE == m_CertType? "Netscape" : "None");
    }

exit:
    ;
}

//+--------------------------------------------------------------------------
// CCertPolicy::_InitSubjectAltNameExtension --
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

VOID
CCertPolicy::_InitSubjectAltNameExtension(
    IN HKEY hkey,
    IN WCHAR const *pwszRegName,
    IN WCHAR const *pwszObjectId,
    IN DWORD iAltName)
{
    DWORD err;
    DWORD dwType;
    DWORD cbbuf;
    WCHAR awcbuf[MAX_PATH];

    cbbuf = sizeof(awcbuf) - 2 * sizeof(WCHAR);
    err = RegQueryValueEx(
    hkey,
    pwszRegName,
    NULL,         // lpdwReserved
    &dwType,
    (BYTE *) awcbuf,
    &cbbuf);
    if (ERROR_SUCCESS != err ||
        REG_SZ != dwType ||
        sizeof(awcbuf) - 2 * sizeof(WCHAR) <= cbbuf)
    {
        goto exit;
    }
    if (0 == lstrcmpi(awcbuf, wszATTREMAIL1) ||
0 == lstrcmpi(awcbuf, wszATTREMAIL2))
    {
        if (!ConvertWszToBstr(
&m_astrSubjectAltNameObjectId[iAltName],
pwszObjectId,
-1))
{
    goto exit;
}

        if (!ConvertWszToBstr(
&m_astrSubjectAltNameProp[iAltName],
wszPROPSUBJECTEMAIL,
-1))
{
    goto exit;
}
    }
    if (fDebug && NULL != m_astrSubjectAltNameProp[iAltName])
    {
        printf(
            "Policy: %ws(RDN=%ws): %ws\n",
    pwszRegName,
            awcbuf,
            m_astrSubjectAltNameProp[iAltName]);
    }

exit:
    ;
}

//+--------------------------------------------------------------------------
// CCertPolicy::Initialize --
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

STDMETHODIMP
CCertPolicy::Initialize(
    /* [in] */ BSTR const strConfig)
{
    HRESULT hr;
    DWORD err;
    HKEY hkey = NULL;
    WCHAR awcReg[MAX_PATH];

    _Cleanup();

    wcscpy(awcReg, wszREGKEYBASE);
    wcscat(awcReg, L"\\");
    wcscat(awcReg, wszREGKEYCONFIG);
    wcscat(awcReg, L"\\");
    wcscat(awcReg, strConfig);

    err = RegOpenKeyEx(
                HKEY_LOCAL_MACHINE,
                awcReg,
                0,              // dwReserved
                KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE,
                &hkey);

    if (ERROR_SUCCESS != err)
    {
hr = HRESULT_FROM_WIN32(err);
        goto exit;
    }

    _InitRevocationExtension(hkey);
    _InitCertTypeExtension(hkey);
    _InitSubjectAltNameExtension(
    hkey,
    wszREGSUBJECTALTNAME,
    TEXT(szOID_SUBJECT_ALT_NAME),
    0);
    _InitSubjectAltNameExtension(
    hkey,
    wszREGSUBJECTALTNAME2,
    TEXT(szOID_SUBJECT_ALT_NAME2),
    1);
    hr = S_OK;

exit:
    if (NULL != hkey)
    {
        RegCloseKey(hkey);
    }
    return(hr);
}

HRESULT
EnumerateExtensions(
    IN ICertServerPolicy *pServer)
{
    HRESULT hr;
    HRESULT hr2;
    BSTR strName = NULL;
    LONG ExtFlags;
    VARIANT varValue;
    BOOL fClose = FALSE;

    VariantInit(&varValue);
    hr = pServer->EnumerateExtensionsSetup(0);
    if (S_OK != hr)
    {
        if (fDebug)
        {
            printf("Policy:EnumerateExtensionsSetup: %x", hr);
        }
        goto error;
    }
    fClose = TRUE;
    while (TRUE)
    {
        hr = pServer->EnumerateExtensions(&strName);
        if (S_OK != hr)
        {
            if (S_FALSE == hr)
            {
                hr = S_OK;
                break;
            }
            if (fDebug)
            {
                printf("Policy:EnumerateExtensions: %x", hr);
            }
            goto error;
        }
        hr = pServer->GetCertificateExtension(
                                        strName,
                                        PROPTYPE_BINARY,
                                        &varValue);
        if (S_OK != hr)
        {
            if (fDebug)
            {
                printf("Policy:GetCertificateExtension: %x", hr);
            }
            goto error;
        }
        hr = pServer->GetCertificateExtensionFlags(&ExtFlags);

        if (S_OK != hr)
        {
            if (fDebug)
            {
                printf("Policy:GetCertificateExtensionFlags: %x", hr);
            }
            goto error;
        }

        // If this is enhanced key usage, add the extension to the cert

        if (0 == lstrcmpi(strName, TEXT(szOID_ENHANCED_KEY_USAGE)))
{
            ExtFlags &= ~EXTENSION_DISABLE_FLAG;
            hr = pServer->SetCertificateExtension(
strName,
PROPTYPE_BINARY,
ExtFlags,
&varValue);

            if (S_OK != hr)
            {
                if (fDebug)
                {
    printf("Policy:SetCertificateExtensionFlags: %x", hr);
                }
                goto error;
            }
        }

        if (fDebug)
        {
            printf(
                "Policy:EnumerateExtensions(%ws, Flags=%x, %x bytes)\n",
                strName,
                ExtFlags,
                SysStringByteLen(varValue.bstrVal));
        }
        VariantClear(&varValue);
    }

error:
    if (fClose)
    {
        hr2 = pServer->EnumerateExtensionsClose();
        if (S_OK != hr2)
        {
            if (fDebug)
            {
                printf("Policy:EnumerateExtensionsClose: %x", hr2);
            }
            if (S_OK == hr)
            {
                hr = hr2;
            }
            goto error;
        }
    }
    if (NULL != strName)
    {
        SysFreeString(strName);
    }
    VariantClear(&varValue);
    return(hr);
}

HRESULT
EnumerateAttributes(
    IN ICertServerPolicy *pServer)
{
    HRESULT hr;
    HRESULT hr2;
    BSTR strName = NULL;
    BOOL fClose = FALSE;
    BSTR strValue = NULL;

    hr = pServer->EnumerateAttributesSetup(0);
    if (S_OK != hr)
    {
        if (fDebug)
        {
            printf("Policy:EnumerateAttributesSetup: %x", hr);
        }
        goto error;
    }
    fClose = TRUE;
    while (TRUE)
    {
        hr = pServer->EnumerateAttributes(&strName);
        if (S_OK != hr)
        {
            if (S_FALSE == hr)
            {
                hr = S_OK;
                break;
            }
            if (fDebug)
            {
                printf("Policy:EnumerateAttributes: %x", hr);
            }
            goto error;
        }

        hr = pServer->GetRequestAttribute(strName, &strValue);
        if (S_OK != hr)
        {
            if (fDebug)
            {
                printf("Policy:GetRequestAttribute: %x", hr);
            }
            goto error;
        }
        if (fDebug)
        {
            printf(
                "Policy:EnumerateAttributes(%ws = %ws)\n",
                strName,
                strValue);
        }
        if (NULL != strValue)
        {
            SysFreeString(strValue);
            strValue = NULL;
        }
    }

error:
    if (fClose)
    {
        hr2 = pServer->EnumerateAttributesClose();
        if (S_OK != hr2)
        {
            if (fDebug)
            {
                printf("Policy:EnumerateAttributesClose: %x", hr2);
            }
            if (S_OK == hr)
            {
                hr = hr2;
            }
            goto error;
        }
    }

    if (NULL != strName)
    {
        SysFreeString(strName);
    }
    if (NULL != strValue)
    {
        SysFreeString(strValue);
    }
    return(hr);
}

HRESULT
CheckRequestProperties(
    IN ICertServerPolicy *pServer)
{
    HRESULT hr;
    VARIANT varValue;
    BSTR strName = NULL;

    VariantInit(&varValue);

    strName = SysAllocString(wszPROPREQUESTREQUESTID);
    if (NULL == strName)
    {
        hr = E_OUTOFMEMORY;
        goto error;
    }

    hr = pServer->GetRequestProperty(strName, PROPTYPE_LONG, &varValue);
    if (S_OK != hr)
    {
        if (fDebug)
        {
            printf("Policy:GetRequestProperty: %x", hr);
        }
        goto error;
    }
    if (fDebug)
    {
        printf(
            "Policy:CheckRequestProperties(%ws = %x)\n",
            strName,
            varValue.lVal);
    }
    VariantClear(&varValue);

error:
    if (NULL != strName)
    {
        SysFreeString(strName);
    }
    return(hr);
}

//+--------------------------------------------------------------------------
// CCertPolicy::_AddRevocationExtension --
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

HRESULT
CCertPolicy::_AddRevocationExtension(
    IN ICertServerPolicy *pServer)
{
    HRESULT hr = S_OK;
    ICertEncodeCRLDistInfo *pCRLDist = NULL;
    BSTR strExtension = NULL;
    VARIANT varExtension;
    DWORD i;

    if (NULL != m_apstrRevocationURL)
    {
        varExtension.vt = VT_BSTR;
        if (REVTYPE_CRLDIST == m_RevocationType)
        {
            hr = CoCreateInstance(
                            CLSID_CCertEncodeCRLDistInfo,
                            NULL,               // pUnkOuter
                            CLSCTX_INPROC_SERVER,
                            IID_ICertEncodeCRLDistInfo,
                            (VOID **) &pCRLDist);
            if (S_OK != hr)
            {
                goto exit;
            }

            hr = pCRLDist->Reset(m_cRevocationURL);
            if (S_OK != hr)
            {
                goto exit;
            }
            for (i = 0; i < m_cRevocationURL; i++)
            {
                DWORD j;

                hr = pCRLDist->SetNameCount(i, 1);
                if (S_OK != hr)
                {
                    goto exit;
                }
                for (j = 0; j < 1; j++)
                {
                    hr = pCRLDist->SetNameEntry(
                                            i,
                                            j,
                                            CERT_ALT_NAME_URL,
                                            m_apstrRevocationURL[i]);
                    if (S_OK != hr)
                    {
                        goto exit;
                    }
                }
            }
            hr = pCRLDist->Encode(&strExtension);
            if (S_OK != hr)
            {
                goto exit;
            }
            varExtension.bstrVal = strExtension;
            hr = pServer->SetCertificateExtension(
                                    TEXT(szOID_CRL_DIST_POINTS),
                                    PROPTYPE_BINARY,
                                    0,
                                    &varExtension);
            if (S_OK != hr)
            {
                goto exit;
            }
        }
        else
        if (REVTYPE_NETSCAPE == m_RevocationType)
        {
            varExtension.bstrVal = m_apstrRevocationURL[0];
            hr = pServer->SetCertificateExtension(
                                    TEXT(szOID_NETSCAPE_REVOCATION_URL),
                                    PROPTYPE_STRING,
                                    0,
                                    &varExtension);
            if (S_OK != hr)
            {
                goto exit;
            }
        }
    }

exit:
    if (NULL != strExtension)
    {
        SysFreeString(strExtension);
    }
    if (NULL != pCRLDist)
    {
        pCRLDist->Release();
    }
    return(hr);
}

#define HIGHBIT(bitno)(1 << (7 - (bitno)))// bit counted from high end

#define SSLBIT_CLIENT((BYTE) HIGHBIT(0))// certified for client auth
#define SSLBIT_SERVER((BYTE) HIGHBIT(1))// certified for server auth
#define SSLBIT_CA((BYTE) HIGHBIT(5))// certified for issuing certs
#define SSLBIT_CA2((BYTE) HIGHBIT(6))// second ca bit

#define NSCERTTYPE_CLIENT  ((BYTE) SSLBIT_CLIENT)
#define NSCERTTYPE_SERVER  ((BYTE) (SSLBIT_SERVER | SSLBIT_CLIENT))
#define NSCERTTYPE_CA   ((BYTE) (SSLBIT_CA | SSLBIT_CA2))

//+--------------------------------------------------------------------------
// CCertPolicy::_AddCertTypeExtension --
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

HRESULT
CCertPolicy::_AddCertTypeExtension(
    IN ICertServerPolicy *pServer)
{
    HRESULT hr = S_OK;
    ICertEncodeBitString *pBitString = NULL;
    BSTR strExtension = NULL;
    VARIANT varExtension;
    BSTR strBitString = NULL;
    BSTR strCertType = NULL;

    if (CERTTYPE_NETSCAPE == m_CertType)
    {
BYTE CertType;

hr = CoCreateInstance(
CLSID_CCertEncodeBitString,
NULL,               // pUnkOuter
CLSCTX_INPROC_SERVER,
IID_ICertEncodeBitString,
(VOID **) &pBitString);
if (S_OK != hr)
{
    goto exit;
}

CertType = NSCERTTYPE_CLIENT;// Default to client auth. cert
        hr = pServer->GetRequestAttribute(L"CertType", &strCertType);
        if (S_OK == hr)
        {
            if (0 == lstrcmpi(strCertType, L"server"))
            {
                CertType = NSCERTTYPE_SERVER;
            }
            else if (0 == lstrcmpi(strCertType, L"ca"))
            {
                CertType = NSCERTTYPE_CA;
            }
        }

        if (!ConvertWszToBstr(
    &strBitString,
    (WCHAR const *) &CertType,
    sizeof(CertType)))
{
    hr = E_OUTOFMEMORY;
    goto exit;
}

hr = pBitString->Encode(
    sizeof(CertType) * 8,
    strBitString,
    &strExtension);
if (S_OK != hr)
{
    goto exit;
}

        varExtension.vt = VT_BSTR;
varExtension.bstrVal = strExtension;
hr = pServer->SetCertificateExtension(
TEXT(szOID_NETSCAPE_CERT_TYPE),
PROPTYPE_BINARY,
0,
&varExtension);
if (S_OK != hr)
{
    goto exit;
}
    }

exit:
    if (NULL != strExtension)
    {
        SysFreeString(strExtension);
    }
    if (NULL != strBitString)
    {
        SysFreeString(strBitString);
    }
    if (NULL != strCertType)
    {
        SysFreeString(strCertType);
    }
    if (NULL != pBitString)
    {
        pBitString->Release();
    }
    return(hr);
}

//+--------------------------------------------------------------------------
// CCertPolicy::_AddSubjectAltNameExtension --
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

HRESULT
CCertPolicy::_AddSubjectAltNameExtension(
    IN ICertServerPolicy *pServer,
    IN DWORD iAltName)
{
    HRESULT hr = S_OK;
    ICertEncodeAltName *pAltName = NULL;
    BSTR strExtension = NULL;
    VARIANT varExtension;
    BSTR strCertType = NULL;
    BSTR strName = NULL;
    VARIANT varValue;

    VariantInit(&varValue);
    if (NULL != m_astrSubjectAltNameProp[iAltName])
    {
hr = CoCreateInstance(
CLSID_CCertEncodeAltName,
NULL,               // pUnkOuter
CLSCTX_INPROC_SERVER,
IID_ICertEncodeAltName,
(VOID **) &pAltName);
if (S_OK != hr)
{
    goto exit;
}

hr = pServer->GetRequestProperty(
    m_astrSubjectAltNameProp[iAltName],
    PROPTYPE_STRING,
    &varValue);
if (S_OK != hr)
{
    if (fDebug)
    {
printf(
    "Policy:GetRequestProperty(%ws): %x",
    m_astrSubjectAltNameProp[iAltName],
    hr);
    }
    goto exit;
}
        if (VT_BSTR != varValue.vt)
{
     hr = E_INVALIDARG;
    goto exit;
}

        if (L'\0' == varValue.bstrVal[0])
{
    hr = S_OK;
    goto exit;
}
        if (!ConvertWszToBstr(&strName, varValue.bstrVal, -1))
{
    hr = E_OUTOFMEMORY;
    goto exit;
}

hr = pAltName->Reset(1);
if (S_OK != hr)
{
    goto exit;
}

hr = pAltName->SetNameEntry(0, CERT_ALT_NAME_RFC822_NAME, strName);
if (S_OK != hr)
{
    goto exit;
}

hr = pAltName->Encode(&strExtension);
if (S_OK != hr)
{
    goto exit;
}

        varExtension.vt = VT_BSTR;
varExtension.bstrVal = strExtension;
hr = pServer->SetCertificateExtension(
m_astrSubjectAltNameObjectId[iAltName],
PROPTYPE_BINARY,
0,
&varExtension);
if (S_OK != hr)
{
    goto exit;
}
    }

exit:
    if (NULL != strExtension)
    {
        SysFreeString(strExtension);
    }
    if (NULL != strName)
    {
        SysFreeString(strName);
    }
    if (NULL != pAltName)
    {
        pAltName->Release();
    }
    VariantClear(&varValue);
    return(hr);
}

//+--------------------------------------------------------------------------
// CCertPolicy::VerifyRequest --
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

STDMETHODIMP
CCertPolicy::VerifyRequest(
    /* [in] */ BSTR const strConfig,
    /* [in] */ LONG Context,
    /* [in] */ LONG bNewRequest,
    /* [in] */ LONG Flags,
    /* [out, retval] */ LONG __RPC_FAR *pDisposition)
{
    HRESULT hr;
    ICertServerPolicy *pServer = NULL;

    hr = CoCreateInstance(
                    CLSID_CCertServerPolicy,
                    NULL,               // pUnkOuter
                    CLSCTX_INPROC_SERVER,
                    IID_ICertServerPolicy,
                    (VOID **) &pServer);
    if (S_OK != hr)
    {
        goto exit;
    }
    hr = pServer->SetContext(Context);
    if (S_OK != hr)
    {
        goto exit;
    }

    hr = _AddRevocationExtension(pServer);
    if (S_OK != hr)
    {
        goto exit;
    }

    hr = _AddCertTypeExtension(pServer);
    if (S_OK != hr)
    {
        goto exit;
    }

    hr = _AddSubjectAltNameExtension(pServer, 0);
    if (S_OK != hr)
    {
        goto exit;
    }

    hr = _AddSubjectAltNameExtension(pServer, 1);
    if (S_OK != hr)
    {
        goto exit;
    }

    hr = EnumerateExtensions(pServer);
    if (S_OK != hr)
    {
        goto exit;
    }

    hr = EnumerateAttributes(pServer);
    if (S_OK != hr)
    {
        goto exit;
    }

    hr = CheckRequestProperties(pServer);
    if (S_OK != hr)
    {
        goto exit;
    }

exit:
    *pDisposition = S_OK == hr? VR_INSTANT_OK : VR_INSTANT_BAD;
    if (NULL != pServer)
    {
        pServer->Release();
    }
    return(hr);
}

//+--------------------------------------------------------------------------
// CCertPolicy::GetDescription --
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

STDMETHODIMP
CCertPolicy::GetDescription(
    /* [out, retval] */ BSTR __RPC_FAR *pstrDescription)
{
    HRESULT hr = S_OK;

    *pstrDescription = SysAllocString(wszDESCRIPTION);
    if (NULL == *pstrDescription)
    {
        hr = E_OUTOFMEMORY;
    }
    return(hr);
}

//+--------------------------------------------------------------------------
// CCertPolicy::ShutDown --
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------

STDMETHODIMP
CCertPolicy::ShutDown(VOID)
{
    return(S_OK);
}

STDMETHODIMP
CCertPolicy::InterfaceSupportsErrorInfo(REFIID riid)
{
    static const IID *arr[] =
    {
        &IID_ICertPolicy,
    };

    for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
    {
        if (InlineIsEqualGUID(*arr[i], riid))
        {
            return(S_OK);
        }
    }
    return(S_FALSE);
}


POLICY.H

// policy.h: Declaration of CCertPolicy

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// certpol

#define REVTYPE_NONE0x00000000
#define REVTYPE_CRLDIST0x00000001
#define REVTYPE_NETSCAPE0x00000003

#define CERTTYPE_NONE           0x00000000
#define CERTTYPE_NETSCAPE       0x00000001

class CCertPolicy:
    public CComDualImpl<ICertPolicy, &IID_ICertPolicy, &LIBID_CERTPOLICYLib>,
    public ISupportErrorInfo,
    public CComObjectRoot,
    public CComCoClass<CCertPolicy, &CLSID_CCertPolicy>
{
public:
    CCertPolicy()
    {
// RevocationExtension variables:

m_RevocationType = REVTYPE_NONE;
m_apstrRevocationURL = NULL;

// CertTypeExtension variables:

m_CertType = CERTTYPE_NONE;

// SubjectAltNameExtension variables:

m_astrSubjectAltNameProp[0] = NULL;
m_astrSubjectAltNameProp[1] = NULL;
m_astrSubjectAltNameObjectId[0] = NULL;
m_astrSubjectAltNameObjectId[1] = NULL;
    }
    ~CCertPolicy();

BEGIN_COM_MAP(CCertPolicy)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ICertPolicy)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()

DECLARE_NOT_AGGREGATABLE(CCertPolicy)
// Remove the comment from the line above if you don't want your object to
// support aggregation.  The default is to support it

DECLARE_REGISTRY(
    CCertPolicy,
    wszCLASS_CERTPOLICY TEXT(".1"),
    wszCLASS_CERTPOLICY,
    IDS_CERTPOLICY_DESC,
    THREADFLAGS_BOTH)

// ISupportsErrorInfo
    STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// ICertPolicy
public:
    STDMETHOD(Initialize)(
    /* [in] */ BSTR const strConfig);

    STDMETHOD(VerifyRequest)(
    /* [in] */ BSTR const strConfig,
    /* [in] */ LONG Context,
    /* [in] */ LONG bNewRequest,
    /* [in] */ LONG Flags,
    /* [out, retval] */ LONG __RPC_FAR *pDisposition);

    STDMETHOD(GetDescription)(
    /* [out, retval] */ BSTR __RPC_FAR *pstrDescription);

    STDMETHOD(ShutDown)();

private:
    VOID _Cleanup();

    VOID _InitRevocationExtension(IN HKEY hkey);
    VOID _InitCertTypeExtension(IN HKEY hkey);
    VOID _InitSubjectAltNameExtension(
IN HKEY hkey,
IN WCHAR const *pwszRegName,
IN WCHAR const *pwszObjectId,
IN DWORD iAltName);

    HRESULT _AddRevocationExtension(IN ICertServerPolicy *pServer);
    HRESULT _AddCertTypeExtension(IN ICertServerPolicy *pServer);
    HRESULT _AddSubjectAltNameExtension(
IN ICertServerPolicy *pServer,
IN DWORD iAltName);

    // Add local variables here!

    // RevocationExtension variables:

    DWORD m_RevocationType;
    DWORD m_cRevocationURL;
    BSTR *m_apstrRevocationURL;

    // CertTypeExtension variables:

    DWORD m_CertType;

    // SubjectAltNameExtension variables:

    BSTR m_astrSubjectAltNameProp[2];
    BSTR m_astrSubjectAltNameObjectId[2];
};


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by certdpol.rc
//
#define IDS_CERTPOLICY_DESC1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

The source code in this directory can be used to build a sample Policy Module
for the Microsoft Certificate Server.  It is meant to run on Windows NT 4.0
with SP3 or later or on Windows NT 5.0 only.  The Certificate Server must
already be installed.

The Certificate Server calls the Policy Module through the ICertPolicy
interface, and the Policy Module can call back to the Certificate Server
through the ICertServerPolicy interface.

Each time the Certificate Server receives a certificate request, it passes
control to the CCertPolicy::VerifyRequest method in policy.cpp.  The passed
Context parameter is used with the ICertServerPolicy interface to retrieve
properties from the request and potential certificate.  The VerifyRequest
method may add, modify or enable Certificate Extensions, modify the NotBefore
and NotAfter dates and Subject name RDN (Relative Distinguished Name) strings
for the potential certificate.  It must also perform any validation required,
and decide the disposition of the request.  The method should return one of
VR_PENDING, VR_INSTANT_OK or VR_INSTANT_BAD to cause the request to be made
pending, to grant the request and issue the certificate, or to fail the
request.

Once the policyvb.dll DLL is built, its COM interface must be registered
via the following command:
    regsvr32 policyvb.dll
the Certificate Server service must then be stopped and restarted as a console
application to load the newly registered Policy Module.  Use the Control
Panel's Services applet, and stop the "Certificate Authority" service, then
start the Certificate Server as a console application via the following
command:
    certsrv -z

NOTE: Because this VB Policy Module uses an Interactive Form to display
information passed in the certificate request, IT MUST BE STARTED VIA THE
ABOVE COMMAND AS A CONSOLE APPLICATION in order to interact with the desktop
to display the form and accept the user's input.

Files:
------
const.bas    -- Constant definitions

main.bas     -- main definition

mssccprj.scc -- stub source code control file

policy.cls   -- Implements ICertPolicy

policyvb.frm -- Form definition

policyvb.vbp -- VB Project file

policyvb.vbw -- VB Workspace file


MAIN.BAS

Attribute VB_Name = "entry"
Sub main()
End Sub


POLICYVB.FRM

VERSION 4.00
Begin VB.Form policyvb
   Caption         =   "Certificate Authority Policy Module"
   ClientHeight    =   7545
   ClientLeft      =   1305
   ClientTop       =   1590
   ClientWidth     =   5790
   Height          =   7950
   Left            =   1245
   LinkTopic       =   "Form1"
   ScaleHeight     =   7545
   ScaleWidth      =   5790
   Top             =   1245
   Width           =   5910
   Begin VB.CommandButton cmdPending
      Caption         =   "Make Pending"
      Height          =   975
      Left            =   2160
      TabIndex        =   2
      Top             =   6240
      Width           =   1455
   End
   Begin VB.TextBox RequestTypeText
      BackColor       =   &H00C0C0C0&
      Height          =   285
      Left            =   3600
      Locked          =   -1  'True
      TabIndex        =   17
      TabStop         =   0   'False
      Top             =   4920
      Width           =   1095
   End
   Begin VB.TextBox VersionText
      BackColor       =   &H00C0C0C0&
      Height          =   285
      Left            =   1200
      Locked          =   -1  'True
      TabIndex        =   16
      TabStop         =   0   'False
      Top             =   4920
      Width           =   495
   End
   Begin VB.TextBox StatusText
      BackColor       =   &H00C0C0C0&
      ForeColor       =   &H00000000&
      Height          =   495
      Left            =   480
      Locked          =   -1  'True
      TabIndex        =   14
      TabStop         =   0   'False
      Text            =   "Status"
      Top             =   5520
      Width           =   4815
   End
   Begin VB.Frame Information
      Caption         =   "Certificate Request Information"
      Height          =   4335
      Left            =   405
      TabIndex        =   13
      Top             =   360
      Width           =   4995
      Begin VB.TextBox Disposition
         Height          =   375
         Left            =   3840
         Locked          =   -1  'True
         TabIndex        =   23
         TabStop         =   0   'False
         Text            =   "TimeOut"
         Top             =   3000
         Visible         =   0   'False
         Width           =   855
      End
      Begin VB.TextBox NotAfterText
         BackColor       =   &H00C0C0C0&
         Height          =   375
         Left            =   3120
         Locked          =   -1  'True
         TabIndex        =   22
         TabStop         =   0   'False
         Text            =   "NotAfter"
         Top             =   3720
         Width           =   1600
      End
      Begin VB.TextBox NotBeforeText
         BackColor       =   &H00C0C0C0&
         Height          =   375
         Left            =   1080
         Locked          =   -1  'True
         TabIndex        =   21
         TabStop         =   0   'False
         Text            =   "NotBefore"
         Top             =   3720
         Width           =   1600
      End
      Begin VB.Timer DisplayTimer
         Enabled         =   0   'False
         Interval        =   3000
         Left            =   4080
         Top             =   2400
      End
      Begin VB.TextBox NameText
         BackColor       =   &H00C0C0C0&
         Height          =   375
         Left            =   2040
         Locked          =   -1  'True
         TabIndex        =   7
         TabStop         =   0   'False
         Top             =   3000
         Width           =   1500
      End
      Begin VB.TextBox CountryText
         BackColor       =   &H00C0C0C0&
         Height          =   375
         Left            =   2040
         Locked          =   -1  'True
         TabIndex        =   6
         TabStop         =   0   'False
         Top             =   2400
         Width           =   1500
      End
      Begin VB.TextBox LocalityText
         BackColor       =   &H00C0C0C0&
         Height          =   375
         Left            =   2040
         Locked          =   -1  'True
         TabIndex        =   5
         TabStop         =   0   'False
         Top             =   1800
         Width           =   1500
      End
      Begin VB.TextBox OrgUnitText
         BackColor       =   &H00C0C0C0&
         Height          =   375
         Left            =   2040
         Locked          =   -1  'True
         TabIndex        =   4
         TabStop         =   0   'False
         Top             =   1200
         Width           =   1500
      End
      Begin VB.TextBox OrgText
         BackColor       =   &H00C0C0C0&
         Height          =   375
         Left            =   2040
         Locked          =   -1  'True
         TabIndex        =   0
         TabStop         =   0   'False
         Top             =   600
         Width           =   1500
      End
      Begin VB.Label ToLabel
         Caption         =   "To"
         Height          =   375
         Left            =   2800
         TabIndex        =   20
         Top             =   3720
         Width           =   615
      End
      Begin VB.Label ValidLabel
         Caption         =   "Valid From"
         Height          =   495
         Left            =   240
         TabIndex        =   19
         Top             =   3720
         Width           =   855
      End
      Begin VB.Label NameLabel
         Caption         =   "Name"
         Height          =   405
         Left            =   240
         TabIndex        =   12
         Top             =   3000
         Width           =   1500
      End
      Begin VB.Label LocalityLabel
         Caption         =   "Locality"
         Height          =   405
         Left            =   240
         TabIndex        =   10
         Top             =   1800
         Width           =   1500
      End
      Begin VB.Label CountryLabel
         Caption         =   "Country"
         Height          =   405
         Left            =   240
         TabIndex        =   11
         Top             =   2400
         Width           =   1500
      End
      Begin VB.Label OrgUnitLabel
         Caption         =   "Organizational Unit"
         Height          =   405
         Left            =   240
         TabIndex        =   9
         Top             =   1200
         Width           =   1500
      End
      Begin VB.Label OrgLabel
         Caption         =   "Organization"
         Height          =   400
         Left            =   240
         TabIndex        =   8
         Top             =   600
         Width           =   1500
      End
   End
   Begin VB.CommandButton cmdDeny
      Cancel          =   -1  'True
      Caption         =   "Deny Certificate"
      Height          =   975
      Left            =   3840
      TabIndex        =   3
      Top             =   6240
      Width           =   1455
   End
   Begin VB.CommandButton cmdIssue
      Caption         =   "Issue Certificate"
      Default         =   -1  'True
      Height          =   975
      Left            =   480
      TabIndex        =   1
      Top             =   6240
      Width           =   1455
   End
   Begin VB.Label RequestTypeLabel
      Caption         =   "RequestType"
      Height          =   375
      Left            =   2520
      TabIndex        =   18
      Top             =   4920
      Width           =   1095
   End
   Begin VB.Label VersionLabel
      Caption         =   "Version"
      Height          =   255
      Left            =   600
      TabIndex        =   15
      Top             =   4920
      Width           =   735
   End
End
Attribute VB_Name = "policyvb"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit

Private Sub cmdDeny_Click()
    Hide
    Disposition.Text = "Deny"
End Sub

Private Sub cmdIssue_Click()
    Hide
    Disposition.Text = "Issue"
End Sub
Private Sub cmdPending_Click()
    Hide
    Disposition.Text = "Pending"
End Sub

Private Sub DisplayTimer_Timer()
    Hide
    Disposition.Text = "TimeOut"
End Sub


CONST.BAS

Attribute VB_Name = "Const"
Option Explicit
'+---------------------------------------------------------------------------
'
' Microsoft Windows
'
' File: wincrypt.h
'
' Contents: Cryptographic API Prototypes and Definitions
'
'----------------------------------------------------------------------------
'
' Algorithm IDs and Flags
'
' ALG_ID crackers
' Algorithm classes
Public Const ALG_CLASS_ANY As Long = 0
' Algorithm types
Public Const ALG_TYPE_ANY As Long = 0
' Generic sub-ids
Public Const ALG_SID_ANY As Long = 0
' Some RSA sub-ids
Public Const ALG_SID_RSA_ANY As Long = 0
Public Const ALG_SID_RSA_PKCS As Long = 1
Public Const ALG_SID_RSA_MSATWORK As Long = 2
Public Const ALG_SID_RSA_ENTRUST As Long = 3
Public Const ALG_SID_RSA_PGP As Long = 4
' Some DSS sub-ids
'
Public Const ALG_SID_DSS_ANY As Long = 0
Public Const ALG_SID_DSS_PKCS As Long = 1
Public Const ALG_SID_DSS_DMS As Long = 2
' Block cipher sub ids
' DES sub_ids
Public Const ALG_SID_DES As Long = 1
Public Const ALG_SID_3DES As Long = 3
Public Const ALG_SID_DESX As Long = 4
Public Const ALG_SID_IDEA As Long = 5
Public Const ALG_SID_CAST As Long = 6
Public Const ALG_SID_SAFERSK64 As Long = 7
Public Const ALD_SID_SAFERSK128 As Long = 8
Public Const ALG_SID_3DES_112 As Long = 9
Public Const ALG_SID_CYLINK_MEK As Long = 12
Public Const ALG_SID_RC5 As Long = 13
' Fortezza sub-ids
Public Const ALG_SID_SKIPJACK As Long = 10
Public Const ALG_SID_TEK As Long = 11
' KP_MODE
Public Const CRYPT_MODE_CBCI As Long = 6
Public Const CRYPT_MODE_CFBP As Long = 7
Public Const CRYPT_MODE_OFBP As Long = 8
Public Const CRYPT_MODE_CBCOFM As Long = 9
Public Const CRYPT_MODE_CBCOFMI As Long = 10
' RC2 sub-ids
Public Const ALG_SID_RC2 As Long = 2
' Stream cipher sub-ids
Public Const ALG_SID_RC4 As Long = 1
Public Const ALG_SID_SEAL As Long = 2
' Diffie-Hellman sub-ids
Public Const ALG_SID_DH_SANDF As Long = 1
Public Const ALG_SID_DH_EPHEM As Long = 2
Public Const ALG_SID_AGREED_KEY_ANY As Long = 3
Public Const ALG_SID_KEA As Long = 4
' Hash sub ids
Public Const ALG_SID_MD2 As Long = 1
Public Const ALG_SID_MD4 As Long = 2
Public Const ALG_SID_MD5 As Long = 3
Public Const ALG_SID_SHA As Long = 4
Public Const ALG_SID_SHA1 As Long = 4
Public Const ALG_SID_MAC As Long = 5
Public Const ALG_SID_RIPEMD As Long = 6
Public Const ALG_SID_RIPEMD160 As Long = 7
Public Const ALG_SID_SSL3SHAMD5 As Long = 8
Public Const ALG_SID_HMAC As Long = 9
' secure channel sub ids
Public Const ALG_SID_SSL3_MASTER As Long = 1
Public Const ALG_SID_SCHANNEL_MASTER_HASH As Long = 2
Public Const ALG_SID_SCHANNEL_MAC_KEY As Long = 3
Public Const ALG_SID_PCT1_MASTER As Long = 4
Public Const ALG_SID_SSL2_MASTER As Long = 5
Public Const ALG_SID_TLS1_MASTER As Long = 6
Public Const ALG_SID_SCHANNEL_ENC_KEY As Long = 7
' Our silly example sub-id
Public Const ALG_SID_EXAMPLE As Long = 80
' algorithm identifier definitions
' dwFlags definitions for CryptAcquireContext
Public Const CRYPT_VERIFYCONTEXT As Long = &HF0000000
Public Const CRYPT_NEWKEYSET As Long = &H00000008
Public Const CRYPT_DELETEKEYSET As Long = &H00000010
Public Const CRYPT_MACHINE_KEYSET As Long = &H00000020
' dwFlag definitions for CryptGenKey
Public Const CRYPT_EXPORTABLE As Long = &H00000001
Public Const CRYPT_USER_PROTECTED As Long = &H00000002
Public Const CRYPT_CREATE_SALT As Long = &H00000004
Public Const CRYPT_UPDATE_KEY As Long = &H00000008
Public Const CRYPT_NO_SALT As Long = &H00000010
Public Const CRYPT_PREGEN As Long = &H00000040
Public Const CRYPT_RECIPIENT As Long = &H00000010
Public Const CRYPT_INITIATOR As Long = &H00000040
Public Const CRYPT_ONLINE As Long = &H00000080
Public Const CRYPT_SF As Long = &H00000100
Public Const CRYPT_CREATE_IV As Long = &H00000200
Public Const CRYPT_KEK As Long = &H00000400
Public Const CRYPT_DATA_KEY As Long = &H00000800
' dwFlags definitions for CryptDeriveKey
Public Const CRYPT_SERVER As Long = &H00000400
Public Const KEY_LENGTH_MASK As Long = &HFFFF0000
' dwFlag definitions for CryptExportKey
Public Const CRYPT_Y_ONLY As Long = &H00000001
Public Const CRYPT_SSL2_SLUMMING As Long = &H00000002
' dwFlags definitions for CryptHashSessionKey
Public Const CRYPT_LITTLE_ENDIAN As Long = &H00000001
' dwFlag definitions for CryptSetProviderEx and CryptGetDefaultProvider
Public Const CRYPT_MACHINE_DEFAULT As Long = &H00000001
Public Const CRYPT_USER_DEFAULT As Long = &H00000002
Public Const CRYPT_DELETE_DEFAULT As Long = &H00000004
' exported key blob definitions
Public Const SIMPLEBLOB As Long = &H1
Public Const PUBLICKEYBLOB As Long = &H6
Public Const PRIVATEKEYBLOB As Long = &H7
Public Const PLAINTEXTKEYBLOB As Long = &H8
Public Const AT_KEYEXCHANGE As Long = 1
Public Const AT_SIGNATURE As Long = 2
Public Const CRYPT_USERDATA As Long = 1
' dwParam
Public Const KP_IV As Long = 1
Public Const KP_SALT As Long = 2
Public Const KP_PADDING As Long = 3
Public Const KP_MODE As Long = 4
Public Const KP_MODE_BITS As Long = 5
Public Const KP_PERMISSIONS As Long = 6
Public Const KP_ALGID As Long = 7
Public Const KP_BLOCKLEN As Long = 8
Public Const KP_KEYLEN As Long = 9
Public Const KP_SALT_EX As Long = 10
Public Const KP_P As Long = 11
Public Const KP_G As Long = 12
Public Const KP_Q As Long = 13
Public Const KP_X As Long = 14
Public Const KP_Y As Long = 15
Public Const KP_RA As Long = 16
Public Const KP_RB As Long = 17
Public Const KP_INFO As Long = 18
Public Const KP_EFFECTIVE_KEYLEN As Long = 19
Public Const KP_SCHANNEL_ALG As Long = 20
Public Const KP_CLIENT_RANDOM As Long = 21
Public Const KP_SERVER_RANDOM As Long = 22
Public Const KP_RP As Long = 23
Public Const KP_PRECOMP_MD5 As Long = 24
Public Const KP_PRECOMP_SHA As Long = 25
Public Const KP_CERTIFICATE As Long = 26
Public Const KP_CLEAR_KEY As Long = 27
Public Const KP_PUB_EX_LEN As Long = 28
Public Const KP_PUB_EX_VAL As Long = 29
' KP_PADDING
Public Const PKCS5_PADDING As Long = 1
Public Const RANDOM_PADDING As Long = 2
Public Const ZERO_PADDING As Long = 3
' KP_MODE
Public Const CRYPT_MODE_CBC As Long = 1
Public Const CRYPT_MODE_ECB As Long = 2
Public Const CRYPT_MODE_OFB As Long = 3
Public Const CRYPT_MODE_CFB As Long = 4
Public Const CRYPT_MODE_CTS As Long = 5
' KP_PERMISSIONS
Public Const CRYPT_ENCRYPT As Long = &H0001
Public Const CRYPT_DECRYPT As Long = &H0002
Public Const CRYPT_EXPORT As Long = &H0004
Public Const CRYPT_READ As Long = &H0008
Public Const CRYPT_WRITE As Long = &H0010
Public Const CRYPT_MAC As Long = &H0020
Public Const CRYPT_EXPORT_KEY As Long = &H0040
Public Const CRYPT_IMPORT_KEY As Long = &H0080
Public Const HP_ALGID As Long = &H0001
Public Const HP_HASHVAL As Long = &H0002
Public Const HP_HASHSIZE As Long = &H0004
Public Const HP_HMAC_INFO As Long = &H0005
'
' CryptGetProvParam
'
Public Const PP_ENUMALGS As Long = 1
Public Const PP_ENUMCONTAINERS As Long = 2
Public Const PP_IMPTYPE As Long = 3
Public Const PP_NAME As Long = 4
Public Const PP_VERSION As Long = 5
Public Const PP_CONTAINER As Long = 6
Public Const PP_CHANGE_PASSWORD As Long = 7
Public Const PP_KEYSET_SEC_DESCR As Long = 8
Public Const PP_CERTCHAIN As Long = 9
Public Const PP_KEY_TYPE_SUBTYPE As Long = 10
Public Const PP_PROVTYPE As Long = 16
Public Const PP_KEYSTORAGE As Long = 17
Public Const PP_APPLI_CERT As Long = 18
Public Const PP_SYM_KEYSIZE As Long = 19
Public Const PP_SESSION_KEYSIZE As Long = 20
Public Const PP_UI_PROMPT As Long = 21
Public Const PP_ENUMALGS_EX As Long = 22
Public Const CRYPT_FIRST As Long = 1
Public Const CRYPT_NEXT As Long = 2
Public Const CRYPT_IMPL_HARDWARE As Long = 1
Public Const CRYPT_IMPL_SOFTWARE As Long = 2
Public Const CRYPT_IMPL_MIXED As Long = 3
Public Const CRYPT_IMPL_UNKNOWN As Long = 4
' key storage flags
Public Const CRYPT_SEC_DESCR As Long = &H00000001
Public Const CRYPT_PSTORE As Long = &H00000002
Public Const CRYPT_UI_PROMPT As Long = &H00000004
' protocol flags
Public Const CRYPT_FLAG_PCT1 As Long = &H0001
Public Const CRYPT_FLAG_SSL2 As Long = &H0002
Public Const CRYPT_FLAG_SSL3 As Long = &H0004
Public Const CRYPT_FLAG_TLS1 As Long = &H0008
'
' CryptSetProvParam
'
Public Const PP_CLIENT_HWND As Long = 1
Public Const PP_CONTEXT_INFO As Long = 11
Public Const PP_KEYEXCHANGE_KEYSIZE As Long = 12
Public Const PP_SIGNATURE_KEYSIZE As Long = 13
Public Const PP_KEYEXCHANGE_ALG As Long = 14
Public Const PP_SIGNATURE_ALG As Long = 15
Public Const PROV_RSA_FULL As Long = 1
Public Const PROV_RSA_SIG As Long = 2
Public Const PROV_DSS As Long = 3
Public Const PROV_FORTEZZA As Long = 4
Public Const PROV_MS_EXCHANGE As Long = 5
Public Const PROV_SSL As Long = 6
Public Const PROV_RSA_SCHANNEL As Long = 12
Public Const PROV_DSS_DH As Long = 13
Public Const PROV_EC_ECDSA As Long = 14
Public Const PROV_EC_ECNR As Long = 15
Public Const PROV_EC_ECDSA_FULL As Long = 16
Public Const PROV_EC_ECNR_FULL As Long = 17
Public Const PROV_SPYRUS_LYNKS As Long = 20
'
' STT defined Providers
'
Public Const PROV_STT_MER As Long = 7
Public Const PROV_STT_ACQ As Long = 8
Public Const PROV_STT_BRND As Long = 9
Public Const PROV_STT_ROOT As Long = 10
Public Const PROV_STT_ISS As Long = 11
'
' Provider friendly names
'
Public Const MS_DEF_PROV_A As String = "Microsoft Base Cryptographic Provider v1.0"
Public Const MS_DEF_PROV_W As String = "Microsoft Base Cryptographic Provider v1.0"
Public Const MS_ENHANCED_PROV_A As String = "Microsoft Enhanced Cryptographic Provider v1.0"
Public Const MS_ENHANCED_PROV_W As String = "Microsoft Enhanced Cryptographic Provider v1.0"
Public Const MS_DEF_DSS_PROV_A As String = "Microsoft Base DSS Cryptographic Provider"
Public Const MS_DEF_DSS_PROV_W As String = "Microsoft Base DSS Cryptographic Provider"
Public Const MAXUIDLEN As Long = 64
Public Const CUR_BLOB_VERSION As Long = 2
' structure for use with CryptSetHashParam with CALG_HMAC
' structure for use with CryptSetKeyParam with KP_SCHANNEL_ALG
' uses of algortihms for SCHANNEL_ALG structure
Public Const SCHANNEL_MAC_KEY As Long = &H00000000
Public Const SCHANNEL_ENC_KEY As Long = &H00000001
'+-------------------------------------------------------------------------
' CRYPTOAPI BLOB definitions
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' In a CRYPT_BIT_BLOB the last byte may contain 0-7 unused bits. Therefore, the
' overall bit length is cbData * 8 - cUnusedBits.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Type used for any algorithm
'
' Where the Parameters CRYPT_OBJID_BLOB is in its encoded representation. For most
'--------------------------------------------------------------------------
' Following are the definitions of various algorithm object identifiers
' RSA
Public Const szOID_RSA As String = "1.2.840.113549"
Public Const szOID_PKCS As String = "1.2.840.113549.1"
Public Const szOID_RSA_HASH As String = "1.2.840.113549.2"
Public Const szOID_RSA_ENCRYPT As String = "1.2.840.113549.3"
Public Const szOID_PKCS_1 As String = "1.2.840.113549.1.1"
Public Const szOID_PKCS_2 As String = "1.2.840.113549.1.2"
Public Const szOID_PKCS_3 As String = "1.2.840.113549.1.3"
Public Const szOID_PKCS_4 As String = "1.2.840.113549.1.4"
Public Const szOID_PKCS_5 As String = "1.2.840.113549.1.5"
Public Const szOID_PKCS_6 As String = "1.2.840.113549.1.6"
Public Const szOID_PKCS_7 As String = "1.2.840.113549.1.7"
Public Const szOID_PKCS_8 As String = "1.2.840.113549.1.8"
Public Const szOID_PKCS_9 As String = "1.2.840.113549.1.9"
Public Const szOID_PKCS_10 As String = "1.2.840.113549.1.10"
Public Const szOID_RSA_RSA As String = "1.2.840.113549.1.1.1"
Public Const szOID_RSA_MD2RSA As String = "1.2.840.113549.1.1.2"
Public Const szOID_RSA_MD4RSA As String = "1.2.840.113549.1.1.3"
Public Const szOID_RSA_MD5RSA As String = "1.2.840.113549.1.1.4"
Public Const szOID_RSA_SHA1RSA As String = "1.2.840.113549.1.1.5"
Public Const szOID_RSA_SETOAEP_RSA As String = "1.2.840.113549.1.1.6"
Public Const szOID_RSA_data As String = "1.2.840.113549.1.7.1"
Public Const szOID_RSA_signedData As String = "1.2.840.113549.1.7.2"
Public Const szOID_RSA_envelopedData As String = "1.2.840.113549.1.7.3"
Public Const szOID_RSA_signEnvData As String = "1.2.840.113549.1.7.4"
Public Const szOID_RSA_digestedData As String = "1.2.840.113549.1.7.5"
Public Const szOID_RSA_hashedData As String = "1.2.840.113549.1.7.5"
Public Const szOID_RSA_encryptedData As String = "1.2.840.113549.1.7.6"
Public Const szOID_RSA_emailAddr As String = "1.2.840.113549.1.9.1"
Public Const szOID_RSA_unstructName As String = "1.2.840.113549.1.9.2"
Public Const szOID_RSA_contentType As String = "1.2.840.113549.1.9.3"
Public Const szOID_RSA_messageDigest As String = "1.2.840.113549.1.9.4"
Public Const szOID_RSA_signingTime As String = "1.2.840.113549.1.9.5"
Public Const szOID_RSA_counterSign As String = "1.2.840.113549.1.9.6"
Public Const szOID_RSA_challengePwd As String = "1.2.840.113549.1.9.7"
Public Const szOID_RSA_unstructAddr As String = "1.2.840.113549.1.9.8"
Public Const szOID_RSA_extCertAttrs As String = "1.2.840.113549.1.9.9"
Public Const szOID_RSA_MD2 As String = "1.2.840.113549.2.2"
Public Const szOID_RSA_MD4 As String = "1.2.840.113549.2.4"
Public Const szOID_RSA_MD5 As String = "1.2.840.113549.2.5"
Public Const szOID_RSA_RC2CBC As String = "1.2.840.113549.3.2"
Public Const szOID_RSA_RC4 As String = "1.2.840.113549.3.4"
' ITU-T UsefulDefinitions
Public Const szOID_DS As String = "2.5"
Public Const szOID_DSALG As String = "2.5.8"
Public Const szOID_DSALG_CRPT As String = "2.5.8.1"
Public Const szOID_DSALG_HASH As String = "2.5.8.2"
Public Const szOID_DSALG_SIGN As String = "2.5.8.3"
Public Const szOID_DSALG_RSA As String = "2.5.8.1.1"
' http:
' http:
Public Const szOID_OIW As String = "1.3.14"
Public Const szOID_OIWSEC As String = "1.3.14.3.2"
Public Const szOID_OIWSEC_md4RSA As String = "1.3.14.3.2.2"
Public Const szOID_OIWSEC_md5RSA As String = "1.3.14.3.2.3"
Public Const szOID_OIWSEC_md4RSA2 As String = "1.3.14.3.2.4"
Public Const szOID_OIWSEC_desECB As String = "1.3.14.3.2.6"
Public Const szOID_OIWSEC_desCBC As String = "1.3.14.3.2.7"
Public Const szOID_OIWSEC_desOFB As String = "1.3.14.3.2.8"
Public Const szOID_OIWSEC_desCFB As String = "1.3.14.3.2.9"
Public Const szOID_OIWSEC_desMAC As String = "1.3.14.3.2.10"
Public Const szOID_OIWSEC_rsaSign As String = "1.3.14.3.2.11"
Public Const szOID_OIWSEC_dsa As String = "1.3.14.3.2.12"
Public Const szOID_OIWSEC_shaDSA As String = "1.3.14.3.2.13"
Public Const szOID_OIWSEC_mdc2RSA As String = "1.3.14.3.2.14"
Public Const szOID_OIWSEC_shaRSA As String = "1.3.14.3.2.15"
Public Const szOID_OIWSEC_dhCommMod As String = "1.3.14.3.2.16"
Public Const szOID_OIWSEC_desEDE As String = "1.3.14.3.2.17"
Public Const szOID_OIWSEC_sha As String = "1.3.14.3.2.18"
Public Const szOID_OIWSEC_mdc2 As String = "1.3.14.3.2.19"
Public Const szOID_OIWSEC_dsaComm As String = "1.3.14.3.2.20"
Public Const szOID_OIWSEC_dsaCommSHA As String = "1.3.14.3.2.21"
Public Const szOID_OIWSEC_rsaXchg As String = "1.3.14.3.2.22"
Public Const szOID_OIWSEC_keyHashSeal As String = "1.3.14.3.2.23"
Public Const szOID_OIWSEC_md2RSASign As String = "1.3.14.3.2.24"
Public Const szOID_OIWSEC_md5RSASign As String = "1.3.14.3.2.25"
Public Const szOID_OIWSEC_sha1 As String = "1.3.14.3.2.26"
Public Const szOID_OIWSEC_dsaSHA1 As String = "1.3.14.3.2.27"
Public Const szOID_OIWSEC_dsaCommSHA1 As String = "1.3.14.3.2.28"
Public Const szOID_OIWSEC_sha1RSASign As String = "1.3.14.3.2.29"
Public Const szOID_OIWDIR As String = "1.3.14.7.2"
Public Const szOID_OIWDIR_CRPT As String = "1.3.14.7.2.1"
Public Const szOID_OIWDIR_HASH As String = "1.3.14.7.2.2"
Public Const szOID_OIWDIR_SIGN As String = "1.3.14.7.2.3"
Public Const szOID_OIWDIR_md2 As String = "1.3.14.7.2.2.1"
Public Const szOID_OIWDIR_md2RSA As String = "1.3.14.7.2.3.1"
' INFOSEC Algorithms
Public Const szOID_INFOSEC As String = "2.16.840.1.101.2.1"
Public Const szOID_INFOSEC_sdnsSignature As String = "2.16.840.1.101.2.1.1.1"
Public Const szOID_INFOSEC_mosaicSignature As String = "2.16.840.1.101.2.1.1.2"
Public Const szOID_INFOSEC_sdnsConfidentiality As String = "2.16.840.1.101.2.1.1.3"
Public Const szOID_INFOSEC_mosaicConfidentiality As String = "2.16.840.1.101.2.1.1.4"
Public Const szOID_INFOSEC_sdnsIntegrity As String = "2.16.840.1.101.2.1.1.5"
Public Const szOID_INFOSEC_mosaicIntegrity As String = "2.16.840.1.101.2.1.1.6"
Public Const szOID_INFOSEC_sdnsTokenProtection As String = "2.16.840.1.101.2.1.1.7"
Public Const szOID_INFOSEC_mosaicTokenProtection As String = "2.16.840.1.101.2.1.1.8"
Public Const szOID_INFOSEC_sdnsKeyManagement As String = "2.16.840.1.101.2.1.1.9"
Public Const szOID_INFOSEC_mosaicKeyManagement As String = "2.16.840.1.101.2.1.1.10"
Public Const szOID_INFOSEC_sdnsKMandSig As String = "2.16.840.1.101.2.1.1.11"
Public Const szOID_INFOSEC_mosaicKMandSig As String = "2.16.840.1.101.2.1.1.12"
Public Const szOID_INFOSEC_SuiteASignature As String = "2.16.840.1.101.2.1.1.13"
Public Const szOID_INFOSEC_SuiteAConfidentiality As String = "2.16.840.1.101.2.1.1.14"
Public Const szOID_INFOSEC_SuiteAIntegrity As String = "2.16.840.1.101.2.1.1.15"
Public Const szOID_INFOSEC_SuiteATokenProtection As String = "2.16.840.1.101.2.1.1.16"
Public Const szOID_INFOSEC_SuiteAKeyManagement As String = "2.16.840.1.101.2.1.1.17"
Public Const szOID_INFOSEC_SuiteAKMandSig As String = "2.16.840.1.101.2.1.1.18"
Public Const szOID_INFOSEC_mosaicUpdatedSig As String = "2.16.840.1.101.2.1.1.19"
Public Const szOID_INFOSEC_mosaicKMandUpdSig As String = "2.16.840.1.101.2.1.1.20"
Public Const szOID_INFOSEC_mosaicUpdatedInteg As String = "2.16.840.1.101.2.1.1.21"
'+-------------------------------------------------------------------------
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Type used for an extension to an encoded content
'
' Where the Value's CRYPT_OBJID_BLOB is in its encoded representation.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' AttributeTypeValue
'
' Where the Value's CRYPT_OBJID_BLOB is in its encoded representation.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Attributes
'
' Where the Value's PATTR_BLOBs are in their encoded representation.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
'
' The interpretation of the Value depends on the dwValueType.
' See below for a list of the types.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CERT_RDN attribute Object Identifiers
'--------------------------------------------------------------------------
' Labeling attribute types:
Public Const szOID_COMMON_NAME As String = "2.5.4.3"
Public Const szOID_SUR_NAME As String = "2.5.4.4"
Public Const szOID_DEVICE_SERIAL_NUMBER As String = "2.5.4.5"
' Geographic attribute types:
Public Const szOID_COUNTRY_NAME As String = "2.5.4.6"
Public Const szOID_LOCALITY_NAME As String = "2.5.4.7"
Public Const szOID_STATE_OR_PROVINCE_NAME As String = "2.5.4.8"
Public Const szOID_STREET_ADDRESS As String = "2.5.4.9"
' Organizational attribute types:
Public Const szOID_ORGANIZATION_NAME As String = "2.5.4.10"
Public Const szOID_ORGANIZATIONAL_UNIT_NAME As String = "2.5.4.11"
Public Const szOID_TITLE As String = "2.5.4.12"
' Explanatory attribute types:
Public Const szOID_DESCRIPTION As String = "2.5.4.13"
Public Const szOID_SEARCH_GUIDE As String = "2.5.4.14"
Public Const szOID_BUSINESS_CATEGORY As String = "2.5.4.15"
' Postal addressing attribute types:
Public Const szOID_POSTAL_ADDRESS As String = "2.5.4.16"
Public Const szOID_POSTAL_CODE As String = "2.5.4.17"
Public Const szOID_POST_OFFICE_BOX As String = "2.5.4.18"
Public Const szOID_PHYSICAL_DELIVERY_OFFICE_NAME As String = "2.5.4.19"
' Telecommunications addressing attribute types:
Public Const szOID_TELEPHONE_NUMBER As String = "2.5.4.20"
Public Const szOID_TELEX_NUMBER As String = "2.5.4.21"
Public Const szOID_TELETEXT_TERMINAL_IDENTIFIER As String = "2.5.4.22"
Public Const szOID_FACSIMILE_TELEPHONE_NUMBER As String = "2.5.4.23"
Public Const szOID_X21_ADDRESS As String = "2.5.4.24"
Public Const szOID_INTERNATIONAL_ISDN_NUMBER As String = "2.5.4.25"
Public Const szOID_REGISTERED_ADDRESS As String = "2.5.4.26"
Public Const szOID_DESTINATION_INDICATOR As String = "2.5.4.27"
' Preference attribute types:
Public Const szOID_PREFERRED_DELIVERY_METHOD As String = "2.5.4.28"
' OSI application attribute types:
Public Const szOID_PRESENTATION_ADDRESS As String = "2.5.4.29"
Public Const szOID_SUPPORTED_APPLICATION_CONTEXT As String = "2.5.4.30"
' Relational application attribute types:
Public Const szOID_MEMBER As String = "2.5.4.31"
Public Const szOID_OWNER As String = "2.5.4.32"
Public Const szOID_ROLE_OCCUPANT As String = "2.5.4.33"
Public Const szOID_SEE_ALSO As String = "2.5.4.34"
' Security attribute types:
Public Const szOID_USER_PASSWORD As String = "2.5.4.35"
Public Const szOID_USER_CERTIFICATE As String = "2.5.4.36"
Public Const szOID_CA_CERTIFICATE As String = "2.5.4.37"
Public Const szOID_AUTHORITY_REVOCATION_LIST As String = "2.5.4.38"
Public Const szOID_CERTIFICATE_REVOCATION_LIST As String = "2.5.4.39"
Public Const szOID_CROSS_CERTIFICATE_PAIR As String = "2.5.4.40"
' Undocumented attribute types???
'#define szOID_??? "2.5.4.41"
Public Const szOID_GIVEN_NAME As String = "2.5.4.42"
Public Const szOID_INITIALS As String = "2.5.4.43"
' Pilot user attribute types:
Public Const szOID_DOMAIN_COMPONENT As String = "0.9.2342.19200300.100.1.25"
'+-------------------------------------------------------------------------
' CERT_RDN Attribute Value Types
'
' For RDN_ENCODED_BLOB, the Value's CERT_RDN_VALUE_BLOB is in its encoded
' representation. Otherwise, its an array of bytes.
'
' For all CERT_RDN types, Value.cbData is always the number of bytes, not
' necessarily the number of elements in the string. For instance,
'
' For CertDecodeName, two 0 bytes are always appended to the end of the
' These added 0 bytes are't included in the BLOB.cbData.
'--------------------------------------------------------------------------
Public Const CERT_RDN_ANY_TYPE As Long = 0
Public Const CERT_RDN_ENCODED_BLOB As Long = 1
Public Const CERT_RDN_OCTET_STRING As Long = 2
Public Const CERT_RDN_NUMERIC_STRING As Long = 3
Public Const CERT_RDN_PRINTABLE_STRING As Long = 4
Public Const CERT_RDN_TELETEX_STRING As Long = 5
Public Const CERT_RDN_T61_STRING As Long = 5
Public Const CERT_RDN_VIDEOTEX_STRING As Long = 6
Public Const CERT_RDN_IA5_STRING As Long = 7
Public Const CERT_RDN_GRAPHIC_STRING As Long = 8
Public Const CERT_RDN_VISIBLE_STRING As Long = 9
Public Const CERT_RDN_ISO646_STRING As Long = 9
Public Const CERT_RDN_GENERAL_STRING As Long = 10
Public Const CERT_RDN_UNIVERSAL_STRING As Long = 11
Public Const CERT_RDN_INT4_STRING As Long = 11
Public Const CERT_RDN_BMP_STRING As Long = 12
Public Const CERT_RDN_UNICODE_STRING As Long = 12
' Macro to check that the dwValueType is a character string and not an
' encoded blob or octet string
'+-------------------------------------------------------------------------
' A CERT_RDN consists of an array of the above attributes
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Information stored in a subject's or issuer's name. The information
' is represented as an array of the above RDNs.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Name attribute value without the Object Identifier
'
' The interpretation of the Value depends on the dwValueType.
' See above for a list of the types.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Public Key Info
'
' The PublicKey is the encoded representation of the information as it is
' stored in the bit string
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Information stored in a certificate
'
' The Issuer, Subject, Algorithm, PublicKey and Extension BLOBs are the
' encoded representation of the information.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate versions
'--------------------------------------------------------------------------
Public Const CERT_V1 As Long = 0
Public Const CERT_V2 As Long = 1
Public Const CERT_V3 As Long = 2
'+-------------------------------------------------------------------------
' Certificate Information Flags
'--------------------------------------------------------------------------
Public Const CERT_INFO_VERSION_FLAG As Long = 1
Public Const CERT_INFO_SERIAL_NUMBER_FLAG As Long = 2
Public Const CERT_INFO_SIGNATURE_ALGORITHM_FLAG As Long = 3
Public Const CERT_INFO_ISSUER_FLAG As Long = 4
Public Const CERT_INFO_NOT_BEFORE_FLAG As Long = 5
Public Const CERT_INFO_NOT_AFTER_FLAG As Long = 6
Public Const CERT_INFO_SUBJECT_FLAG As Long = 7
Public Const CERT_INFO_SUBJECT_PUBLIC_KEY_INFO_FLAG As Long = 8
Public Const CERT_INFO_ISSUER_UNIQUE_ID_FLAG As Long = 9
Public Const CERT_INFO_SUBJECT_UNIQUE_ID_FLAG As Long = 10
Public Const CERT_INFO_EXTENSION_FLAG As Long = 11
'+-------------------------------------------------------------------------
' An entry in a CRL
'
' The Extension BLOBs are the encoded representation of the information.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Information stored in a CRL
'
' The Issuer, Algorithm and Extension BLOBs are the encoded
' representation of the information.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CRL versions
'--------------------------------------------------------------------------
Public Const CRL_V1 As Long = 0
Public Const CRL_V2 As Long = 1
'+-------------------------------------------------------------------------
' Information stored in a certificate request
'
' The Subject, Algorithm, PublicKey and Attribute BLOBs are the encoded
' representation of the information.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate Request versions
'--------------------------------------------------------------------------
Public Const CERT_REQUEST_V1 As Long = 0
'+-------------------------------------------------------------------------
' Information stored in Netscape's Keygen request
'--------------------------------------------------------------------------
Public Const CERT_KEYGEN_REQUEST_V1 As Long = 0
'+-------------------------------------------------------------------------
' Certificate, CRL, Certificate Request or Keygen Request Signed Content
'
' The "to be signed" encoded content plus its signature. The ToBeSigned
' is the encoded CERT_INFO, CRL_INFO, CERT_REQUEST_INFO or
' CERT_KEYGEN_REQUEST_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CTL Usage. Also used for EnhancedKeyUsage extension.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' An entry in a CTL
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Information stored in a CTL
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CTL versions
'--------------------------------------------------------------------------
Public Const CTL_V1 As Long = 0
'+-------------------------------------------------------------------------
' TimeStamp Request
'
' The pszTimeStamp is the OID for the Time type requested
' The pszContentType is the Content Type OID for the content, usually DATA
' The Content is a un-decoded blob
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate and Message encoding types
'
' The encoding type is a DWORD containing both the certificate and message
' encoding types. The certificate encoding type is stored in the LOWORD.
' The message encoding type is stored in the HIWORD. Some functions or
' structure fields require only one of the encoding types. The following
' required:
'
' Its always acceptable to specify both.
'--------------------------------------------------------------------------
Public Const CERT_ENCODING_TYPE_MASK As Long = &H0000FFFF
Public Const CMSG_ENCODING_TYPE_MASK As Long = &HFFFF0000

Public Const CRYPT_ASN_ENCODING As Long = &H00000001
Public Const CRYPT_NDR_ENCODING As Long = &H00000002
Public Const X509_ASN_ENCODING As Long = &H00000001
Public Const X509_NDR_ENCODING As Long = &H00000002
Public Const PKCS_7_ASN_ENCODING As Long = &H00010000
Public Const PKCS_7_NDR_ENCODING As Long = &H00020000
'+-------------------------------------------------------------------------
' format the specified data structure according to the certificate
' encoding type.
'
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Encode / decode the specified data structure according to the certificate
' encoding type.
'
' See below for a list of the predefined data structures.
'--------------------------------------------------------------------------
' When the following flag is set the nocopy optimization is enabled.
' This optimization where appropriate, updates the pvStructInfo fields
' to point to content residing within pbEncoded instead of making a copy
' of and appending to pvStructInfo.
'
' Note, when set, pbEncoded can't be freed until pvStructInfo is freed.
Public Const CRYPT_DECODE_NOCOPY_FLAG As Long = &H1
'+-------------------------------------------------------------------------
' Predefined X509 certificate data structures that can be encoded / decoded.
'--------------------------------------------------------------------------
Public Const CRYPT_ENCODE_DECODE_NONE As Long = 0
'+-------------------------------------------------------------------------
' Predefined X509 certificate extension data structures that can be
' encoded / decoded.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Additional predefined data structures that can be encoded / decoded.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Predefined primitive data structures that can be encoded / decoded.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' More predefined X509 certificate extension data structures that can be
' encoded / decoded.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Predefined PKCS #7 data structures that can be encoded / decoded.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' can be encoded / decoded.
'
' Predefined values: 2000 .. 2999
'
' See spc.h for value and data structure definitions.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Extension Object Identifiers
'--------------------------------------------------------------------------
Public Const szOID_AUTHORITY_KEY_IDENTIFIER As String = "2.5.29.1"
Public Const szOID_KEY_ATTRIBUTES As String = "2.5.29.2"
Public Const szOID_KEY_USAGE_RESTRICTION As String = "2.5.29.4"
Public Const szOID_SUBJECT_ALT_NAME As String = "2.5.29.7"
Public Const szOID_ISSUER_ALT_NAME As String = "2.5.29.8"
Public Const szOID_BASIC_CONSTRAINTS As String = "2.5.29.10"
Public Const szOID_KEY_USAGE As String = "2.5.29.15"
Public Const szOID_BASIC_CONSTRAINTS2 As String = "2.5.29.19"
Public Const szOID_CERT_POLICIES As String = "2.5.29.32"
Public Const szOID_AUTHORITY_KEY_IDENTIFIER2 As String = "2.5.29.35"
Public Const szOID_SUBJECT_KEY_IDENTIFIER As String = "2.5.29.14"
Public Const szOID_SUBJECT_ALT_NAME2 As String = "2.5.29.17"
Public Const szOID_ISSUER_ALT_NAME2 As String = "2.5.29.18"
Public Const szOID_CRL_REASON_CODE As String = "2.5.29.21"
Public Const szOID_CRL_DIST_POINTS As String = "2.5.29.31"
Public Const szOID_ENHANCED_KEY_USAGE As String = "2.5.29.37"
' Internet Public Key Infrastructure
Public Const szOID_PKIX As String = "1.3.6.1.5.5.7"
Public Const szOID_AUTHORITY_INFO_ACCESS As String = "1.3.6.1.5.5.7.2"
' Microsoft extensions or attributes
Public Const szOID_CERT_EXTENSIONS As String = "1.3.6.1.4.1.311.2.1.14"
Public Const szOID_NEXT_UPDATE_LOCATION As String = "1.3.6.1.4.1.311.10.2"
' Microsoft PKCS #7 ContentType Object Identifiers
Public Const szOID_CTL As String = "1.3.6.1.4.1.311.10.1"
'+-------------------------------------------------------------------------
'--------------------------------------------------------------------------
Public Const szOID_POLICY_MAPPINGS As String = "2.5.29.5"
Public Const szOID_SUBJECT_DIR_ATTRS As String = "2.5.29.9"
'+-------------------------------------------------------------------------
'--------------------------------------------------------------------------
Public Const szOID_PKIX_KP As String = "1.3.6.1.5.5.7.3"
' Consistent key usage bits: DIGITAL_SIGNATURE, KEY_ENCIPHERMENT
' or KEY_AGREEMENT
Public Const szOID_PKIX_KP_SERVER_AUTH As String = "1.3.6.1.5.5.7.3.1"
' Consistent key usage bits: DIGITAL_SIGNATURE
Public Const szOID_PKIX_KP_CLIENT_AUTH As String = "1.3.6.1.5.5.7.3.2"
' Consistent key usage bits: DIGITAL_SIGNATURE
Public Const szOID_PKIX_KP_CODE_SIGNING As String = "1.3.6.1.5.5.7.3.3"
' Consistent key usage bits: DIGITAL_SIGNATURE, NON_REPUDIATION and/or
Public Const szOID_PKIX_KP_EMAIL_PROTECTION As String = "1.3.6.1.5.5.7.3.4"
'+-------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Signer of CTLs
Public Const szOID_KP_CTL_USAGE_SIGNING As String = "1.3.6.1.4.1.311.10.3.1"
'+-------------------------------------------------------------------------
' Microsoft Attribute Object Identifiers
'+-------------------------------------------------------------------------
Public Const szOID_YESNO_TRUST_ATTR As String = "1.3.6.1.4.1.311.10.4.1"
'+-------------------------------------------------------------------------
' X509_CERT
'
' The "to be signed" encoded content plus its signature. The ToBeSigned
' X509_CERT_TO_BE_SIGNED, X509_CERT_CRL_TO_BE_SIGNED or
' X509_CERT_REQUEST_TO_BE_SIGNED.
'
' pvStructInfo points to CERT_SIGNED_CONTENT_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_CERT_TO_BE_SIGNED
'
' pvStructInfo points to CERT_INFO.
'
'
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_CERT_CRL_TO_BE_SIGNED
'
' pvStructInfo points to CRL_INFO.
'
'
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_CERT_REQUEST_TO_BE_SIGNED
'
' pvStructInfo points to CERT_REQUEST_INFO.
'
'
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_EXTENSIONS
' szOID_CERT_EXTENSIONS
'
' pvStructInfo points to following CERT_EXTENSIONS.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_NAME_VALUE
' X509_ANY_STRING
'
' pvStructInfo points to CERT_NAME_VALUE.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_UNICODE_NAME_VALUE
' X509_UNICODE_ANY_STRING
'
' pvStructInfo points to CERT_NAME_VALUE.
'
' The name values are unicode strings.
'
' For CryptEncodeObject:
' Value.pbData points to the unicode string.
' If Value.cbData = 0, then, the unicode string is NULL terminated.
' Otherwise, Value.cbData is the unicode string byte count. The byte count
' is twice the character count.
'
' If the unicode string contains an invalid character for the specified
' dwValueType, then, *pcbEncoded is updated with the unicode character
' index of the first invalid character. LastError is set to:
' CRYPT_E_INVALID_NUMERIC_STRING, CRYPT_E_INVALID_PRINTABLE_STRING or
' CRYPT_E_INVALID_IA5_STRING.
'
' The unicode string is converted before being encoded according to
' the specified dwValueType. If dwValueType is set to 0, LastError
' is set to E_INVALIDARG.
'
' CERT_RDN_ENCODED_BLOB or CERT_RDN_OCTET_STRING), then, CryptEncodeObject
' will return FALSE with LastError set to CRYPT_E_NOT_CHAR_STRING.
'
' For CryptDecodeObject:
' Value.pbData points to a NULL terminated unicode string. Value.cbData
' contains the byte count of the unicode string excluding the NULL
' terminator. dwValueType contains the type used in the encoded object.
' Its not forced to CERT_RDN_UNICODE_STRING. The encoded value is
' converted to the unicode string according to the dwValueType.
'
' If the encoded object isn't one of the character string types, then,
' CryptDecodeObject will return FALSE with LastError set to
' CRYPT_E_NOT_CHAR_STRING. For a non character string, decode using
' X509_NAME_VALUE or X509_ANY_STRING.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_NAME
'
' pvStructInfo points to CERT_NAME_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_UNICODE_NAME
'
' pvStructInfo points to CERT_NAME_INFO.
'
' The RDN attribute values are unicode strings except for the dwValueTypes of
' CERT_RDN_ENCODED_BLOB or CERT_RDN_OCTET_STRING. These dwValueTypes are
' the same as for a X509_NAME. Their values aren't converted to/from unicode.
'
' For CryptEncodeObject:
' Value.pbData points to the unicode string.
' If Value.cbData = 0, then, the unicode string is NULL terminated.
' Otherwise, Value.cbData is the unicode string byte count. The byte count
' is twice the character count.
'
' an acceptable dwValueType. If the unicode string contains an
' invalid character for the found or specified dwValueType, then,
' *pcbEncoded is updated with the error location of the invalid character.
' See below for details. LastError is set to:
' CRYPT_E_INVALID_NUMERIC_STRING, CRYPT_E_INVALID_PRINTABLE_STRING or
' CRYPT_E_INVALID_IA5_STRING.
'
' The unicode string is converted before being encoded according to
' the specified or ObjId matching dwValueType.
'
' For CryptDecodeObject:
' Value.pbData points to a NULL terminated unicode string. Value.cbData
' contains the byte count of the unicode string excluding the NULL
' terminator. dwValueType contains the type used in the encoded object.
' Its not forced to CERT_RDN_UNICODE_STRING. The encoded value is
' converted to the unicode string according to the dwValueType.
'
' If the dwValueType of the encoded value isn't a character string
' type, then, it isn't converted to UNICODE. Use the
' that Value.pbData points to a converted unicode string.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Unicode Name Value Error Location Definitions
'
' Error location is returned in *pcbEncoded by
'
' Error location consists of:
' RDN_INDEX - 10 bits << 22
' ATTR_INDEX - 6 bits << 16
'--------------------------------------------------------------------------
Public Const CERT_UNICODE_RDN_ERR_INDEX_MASK As Long = &H3FF
Public Const CERT_UNICODE_RDN_ERR_INDEX_SHIFT As Long = 22
Public Const CERT_UNICODE_ATTR_ERR_INDEX_MASK As Long = &H003F
Public Const CERT_UNICODE_ATTR_ERR_INDEX_SHIFT As Long = 16
Public Const CERT_UNICODE_VALUE_ERR_INDEX_MASK As Long = &H0000FFFF
Public Const CERT_UNICODE_VALUE_ERR_INDEX_SHIFT As Long = 0
'+-------------------------------------------------------------------------
' X509_PUBLIC_KEY_INFO
'
' pvStructInfo points to CERT_PUBLIC_KEY_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_AUTHORITY_KEY_ID
' szOID_AUTHORITY_KEY_IDENTIFIER
'
' pvStructInfo points to following CERT_AUTHORITY_KEY_ID_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_KEY_ATTRIBUTES
' szOID_KEY_ATTRIBUTES
'
' pvStructInfo points to following CERT_KEY_ATTRIBUTES_INFO.
'--------------------------------------------------------------------------
Public Const CERT_DIGITAL_SIGNATURE_KEY_USAGE As Long = &H80
Public Const CERT_NON_REPUDIATION_KEY_USAGE As Long = &H40
Public Const CERT_KEY_ENCIPHERMENT_KEY_USAGE As Long = &H20
Public Const CERT_DATA_ENCIPHERMENT_KEY_USAGE As Long = &H10
Public Const CERT_KEY_AGREEMENT_KEY_USAGE As Long = &H08
Public Const CERT_KEY_CERT_SIGN_KEY_USAGE As Long = &H04
Public Const CERT_OFFLINE_CRL_SIGN_KEY_USAGE As Long = &H02
Public Const CERT_CRL_SIGN_KEY_USAGE As Long = &H02
'+-------------------------------------------------------------------------
' X509_KEY_USAGE_RESTRICTION
' szOID_KEY_USAGE_RESTRICTION
'
' pvStructInfo points to following CERT_KEY_USAGE_RESTRICTION_INFO.
'--------------------------------------------------------------------------
' See CERT_KEY_ATTRIBUTES_INFO for definition of the RestrictedKeyUsage bits
'+-------------------------------------------------------------------------
' X509_ALTERNATE_NAME
' szOID_SUBJECT_ALT_NAME
' szOID_ISSUER_ALT_NAME
' szOID_SUBJECT_ALT_NAME2
' szOID_ISSUER_ALT_NAME2
'
' pvStructInfo points to following CERT_ALT_NAME_INFO.
'--------------------------------------------------------------------------
Public Const CERT_ALT_NAME_OTHER_NAME As Long = 1
Public Const CERT_ALT_NAME_RFC822_NAME As Long = 2
Public Const CERT_ALT_NAME_DNS_NAME As Long = 3
Public Const CERT_ALT_NAME_X400_ADDRESS As Long = 4
Public Const CERT_ALT_NAME_DIRECTORY_NAME As Long = 5
Public Const CERT_ALT_NAME_EDI_PARTY_NAME As Long = 6
Public Const CERT_ALT_NAME_URL As Long = 7
Public Const CERT_ALT_NAME_IP_ADDRESS As Long = 8
Public Const CERT_ALT_NAME_REGISTERED_ID As Long = 9
'+-------------------------------------------------------------------------
' Alternate name IA5 Error Location Definitions for
' CRYPT_E_INVALID_IA5_STRING.
'
' Error location is returned in *pcbEncoded by
'
' Error location consists of:
' ENTRY_INDEX - 8 bits << 16
'--------------------------------------------------------------------------
Public Const CERT_ALT_NAME_ENTRY_ERR_INDEX_MASK As Long = &HFF
Public Const CERT_ALT_NAME_ENTRY_ERR_INDEX_SHIFT As Long = 16
Public Const CERT_ALT_NAME_VALUE_ERR_INDEX_MASK As Long = &H0000FFFF
Public Const CERT_ALT_NAME_VALUE_ERR_INDEX_SHIFT As Long = 0
'+-------------------------------------------------------------------------
' X509_BASIC_CONSTRAINTS
' szOID_BASIC_CONSTRAINTS
'
' pvStructInfo points to following CERT_BASIC_CONSTRAINTS_INFO.
'--------------------------------------------------------------------------
Public Const CERT_CA_SUBJECT_FLAG As Long = &H80
Public Const CERT_END_ENTITY_SUBJECT_FLAG As Long = &H40
'+-------------------------------------------------------------------------
' X509_BASIC_CONSTRAINTS2
' szOID_BASIC_CONSTRAINTS2
'
' pvStructInfo points to following CERT_BASIC_CONSTRAINTS2_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_KEY_USAGE
' szOID_KEY_USAGE
'
' pvStructInfo points to a CRYPT_BIT_BLOB. Has same bit definitions as
' CERT_KEY_ATTRIBUTES_INFO's IntendedKeyUsage.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_CERT_POLICIES
' szOID_CERT_POLICIES
'
' pvStructInfo points to following CERT_POLICIES_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' RSA_CSP_PUBLICKEYBLOB
'
' pvStructInfo points to a PUBLICKEYSTRUC immediately followed by a
' RSAPUBKEY and the modulus bytes.
'
' CryptExportKey outputs the above StructInfo for a dwBlobType of
' PUBLICKEYBLOB. CryptImportKey expects the above StructInfo when
' importing a public key.
'
' For dwCertEncodingType = X509_ASN_ENCODING, the RSA_CSP_PUBLICKEYBLOB is
' encoded as a PKCS #1 RSAPublicKey consisting of a SEQUENCE of a
' modulus INTEGER and a publicExponent INTEGER. The modulus is encoded
' as being a unsigned integer. When decoded, if the modulus was encoded
' as unsigned integer with a leading 0 byte, the 0 byte is removed before
' converting to the CSP modulus bytes.
'
' For decode, the aiKeyAlg field of PUBLICKEYSTRUC is always set to
' CALG_RSA_KEYX.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_KEYGEN_REQUEST_TO_BE_SIGNED
'
' pvStructInfo points to CERT_KEYGEN_REQUEST_INFO.
'
'
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' PKCS_ATTRIBUTE data structure
'
' pvStructInfo points to a CRYPT_ATTRIBUTE.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' PKCS_CONTENT_INFO_SEQUENCE_OF_ANY data structure
'
' pvStructInfo points to following CRYPT_CONTENT_INFO_SEQUENCE_OF_ANY.
'
' For X509_ASN_ENCODING: encoded as a PKCS#7 ContentInfo structure wrapping
' a sequence of ANY. The value of the contentType field is pszObjId,
' while the content field is the following structure:
' SequenceOfAny ::= SEQUENCE OF ANY
'
' The CRYPT_DER_BLOBs point to the already encoded ANY content.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' PKCS_CONTENT_INFO data structure
'
' pvStructInfo points to following CRYPT_CONTENT_INFO.
'
' For X509_ASN_ENCODING: encoded as a PKCS#7 ContentInfo structure.
' The CRYPT_DER_BLOB points to the already encoded ANY content.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_OCTET_STRING data structure
'
' pvStructInfo points to a CRYPT_DATA_BLOB.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_BITS data structure
'
' pvStructInfo points to a CRYPT_BIT_BLOB.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_INTEGER data structure
'
' pvStructInfo points to an int.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_MULTI_BYTE_INTEGER data structure
'
' pvStructInfo points to a CRYPT_INTEGER_BLOB.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_ENUMERATED data structure
'
' pvStructInfo points to an int containing the enumerated value
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_CHOICE_OF_TIME data structure
'
' pvStructInfo points to a FILETIME.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_SEQUENCE_OF_ANY data structure
'
' pvStructInfo points to following CRYPT_SEQUENCE_OF_ANY.
'
' The CRYPT_DER_BLOBs point to the already encoded ANY content.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_AUTHORITY_KEY_ID2
' szOID_AUTHORITY_KEY_IDENTIFIER2
'
' pvStructInfo points to following CERT_AUTHORITY_KEY_ID2_INFO.
'
' For CRYPT_E_INVALID_IA5_STRING, the error location is returned in
'
' See X509_ALTERNATE_NAME for error location defines.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' szOID_SUBJECT_KEY_IDENTIFIER
'
' pvStructInfo points to a CRYPT_DATA_BLOB.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' X509_CRL_REASON_CODE
' szOID_CRL_REASON_CODE
'
' pvStructInfo points to an int which can be set to one of the following
' enumerated values:
'--------------------------------------------------------------------------
Public Const CRL_REASON_UNSPECIFIED As Long = 0
Public Const CRL_REASON_KEY_COMPROMISE As Long = 1
Public Const CRL_REASON_CA_COMPROMISE As Long = 2
Public Const CRL_REASON_AFFILIATION_CHANGED As Long = 3
Public Const CRL_REASON_SUPERSEDED As Long = 4
Public Const CRL_REASON_CESSATION_OF_OPERATION As Long = 5
Public Const CRL_REASON_CERTIFICATE_HOLD As Long = 6
Public Const CRL_REASON_REMOVE_FROM_CRL As Long = 8
'+-------------------------------------------------------------------------
' X509_CRL_DIST_POINTS
' szOID_CRL_DIST_POINTS
'
' pvStructInfo points to following CRL_DIST_POINTS_INFO.
'
' For CRYPT_E_INVALID_IA5_STRING, the error location is returned in
'
' Error location consists of:
' POINT_INDEX - 7 bits << 24
' ENTRY_INDEX - 8 bits << 16
'
' See X509_ALTERNATE_NAME for ENTRY_INDEX and VALUE_INDEX error location
' defines.
'--------------------------------------------------------------------------
Public Const CRL_DIST_POINT_NO_NAME As Long = 0
Public Const CRL_DIST_POINT_FULL_NAME As Long = 1
Public Const CRL_DIST_POINT_ISSUER_RDN_NAME As Long = 2
Public Const CRL_REASON_UNUSED_FLAG As Long = &H80
Public Const CRL_REASON_KEY_COMPROMISE_FLAG As Long = &H40
Public Const CRL_REASON_CA_COMPROMISE_FLAG As Long = &H20
Public Const CRL_REASON_AFFILIATION_CHANGED_FLAG As Long = &H10
Public Const CRL_REASON_SUPERSEDED_FLAG As Long = &H08
Public Const CRL_REASON_CESSATION_OF_OPERATION_FLAG As Long = &H04
Public Const CRL_REASON_CERTIFICATE_HOLD_FLAG As Long = &H02
Public Const CRL_DIST_POINT_ERR_INDEX_MASK As Long = &H7F
Public Const CRL_DIST_POINT_ERR_INDEX_SHIFT As Long = 24
'+-------------------------------------------------------------------------
' X509_ENHANCED_KEY_USAGE
' szOID_ENHANCED_KEY_USAGE
'
' pvStructInfo points to a CERT_ENHKEY_USAGE, CTL_USAGE.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' szOID_NEXT_UPDATE_LOCATION
'
' pvStructInfo points to a CERT_ALT_NAME_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' PKCS_CTL
' szOID_CTL
'
' pvStructInfo points to a CTL_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' PKCS7_SIGNER_INFO
'
' pvStructInfo points to CMSG_SIGNER_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Netscape Certificate Extension Object Identifiers
'--------------------------------------------------------------------------
Public Const szOID_NETSCAPE As String = "2.16.840.1.113730"
Public Const szOID_NETSCAPE_CERT_EXTENSION As String = "2.16.840.1.113730.1"
Public Const szOID_NETSCAPE_CERT_TYPE As String = "2.16.840.1.113730.1.1"
Public Const szOID_NETSCAPE_BASE_URL As String = "2.16.840.1.113730.1.2"
Public Const szOID_NETSCAPE_REVOCATION_URL As String = "2.16.840.1.113730.1.3"
Public Const szOID_NETSCAPE_CA_REVOCATION_URL As String = "2.16.840.1.113730.1.4"
Public Const szOID_NETSCAPE_CERT_RENEWAL_URL As String = "2.16.840.1.113730.1.7"
Public Const szOID_NETSCAPE_CA_POLICY_URL As String = "2.16.840.1.113730.1.8"
Public Const szOID_NETSCAPE_SSL_SERVER_NAME As String = "2.16.840.1.113730.1.12"
Public Const szOID_NETSCAPE_COMMENT As String = "2.16.840.1.113730.1.13"
'+-------------------------------------------------------------------------
' Netscape Certificate Data Type Object Identifiers
'--------------------------------------------------------------------------
Public Const szOID_NETSCAPE_DATA_TYPE As String = "2.16.840.1.113730.2"
Public Const szOID_NETSCAPE_CERT_SEQUENCE As String = "2.16.840.1.113730.2.5"
'+-------------------------------------------------------------------------
' szOID_NETSCAPE_CERT_TYPE extension
'
' Its value is a bit string. CryptDecodeObject/CryptEncodeObject using
' X509_BITS.
'
' The following bits are defined:
'--------------------------------------------------------------------------
Public Const NETSCAPE_SSL_CLIENT_AUTH_CERT_TYPE As Long = &H80
Public Const NETSCAPE_SSL_SERVER_AUTH_CERT_TYPE As Long = &H40
Public Const NETSCAPE_SSL_CA_CERT_TYPE As Long = &H04
'+-------------------------------------------------------------------------
' szOID_NETSCAPE_BASE_URL extension
'
' Its value is an IA5_STRING. CryptDecodeObject/CryptEncodeObject using
' X509_ANY_STRING or X509_UNICODE_ANY_STRING, where,
' dwValueType = CERT_RDN_IA5_STRING.
'
' When present this string is added to the beginning of all relative URLs
' in the certificate. This extension can be considered an optimization
' to reduce the size of the URL extensions.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' szOID_NETSCAPE_REVOCATION_URL extension
'
' Its value is an IA5_STRING. CryptDecodeObject/CryptEncodeObject using
' X509_ANY_STRING or X509_UNICODE_ANY_STRING, where,
' dwValueType = CERT_RDN_IA5_STRING.
'
' It is a relative or absolute URL that can be used to check the
' revocation status of a certificate. The revocation check will be
' performed as an HTTP GET method using a url that is the concatenation of
' revocation-url and certificate-serial-number.
' Where the certificate-serial-number is encoded as a string of
' ascii hexadecimal digits. For example, if the netscape-base-url is
' https:
' cgi-bin/check-rev.cgi?, and the certificate serial number is 173420,
' the resulting URL would be:
' https:
'
' The server should return a document with a Content-Type of
' application/x-netscape-revocation. The document should contain
' a single ascii digit, '1' if the certificate is not curently valid,
' and '0' if it is curently valid.
'
' Note: for all of the URLs that include the certificate serial number,
' the serial number will be encoded as a string which consists of an even
' number of hexadecimal digits. If the number of significant digits is odd,
' the string will have a single leading zero to ensure an even number of
' digits is generated.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' szOID_NETSCAPE_CA_REVOCATION_URL extension
'
' Its value is an IA5_STRING. CryptDecodeObject/CryptEncodeObject using
' X509_ANY_STRING or X509_UNICODE_ANY_STRING, where,
' dwValueType = CERT_RDN_IA5_STRING.
'
' It is a relative or absolute URL that can be used to check the
' revocation status of any certificates that are signed by the CA that
' this certificate belongs to. This extension is only valid in CA
' certificates. The use of this extension is the same as the above
' szOID_NETSCAPE_REVOCATION_URL extension.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' szOID_NETSCAPE_CERT_RENEWAL_URL extension
'
' Its value is an IA5_STRING. CryptDecodeObject/CryptEncodeObject using
' X509_ANY_STRING or X509_UNICODE_ANY_STRING, where,
' dwValueType = CERT_RDN_IA5_STRING.
'
' It is a relative or absolute URL that points to a certificate renewal
' form. The renewal form will be accessed with an HTTP GET method using a
' url that is the concatenation of renewal-url and
' certificate-serial-number. Where the certificate-serial-number is
' encoded as a string of ascii hexadecimal digits. For example, if the
' netscape-base-url is https:
' netscape-cert-renewal-url is cgi-bin/check-renew.cgi?, and the
' certificate serial number is 173420, the resulting URL would be:
' https:
' The document returned should be an HTML form that will allow the user
' to request a renewal of their certificate.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' szOID_NETSCAPE_CA_POLICY_URL extension
'
' Its value is an IA5_STRING. CryptDecodeObject/CryptEncodeObject using
' X509_ANY_STRING or X509_UNICODE_ANY_STRING, where,
' dwValueType = CERT_RDN_IA5_STRING.
'
' It is a relative or absolute URL that points to a web page that
' describes the policies under which the certificate was issued.
'--------------------------------------------------------------------------

'+-------------------------------------------------------------------------
' szOID_NETSCAPE_SSL_SERVER_NAME extension
'
' Its value is an IA5_STRING. CryptDecodeObject/CryptEncodeObject using
' X509_ANY_STRING or X509_UNICODE_ANY_STRING, where,
' dwValueType = CERT_RDN_IA5_STRING.
'
' It is a "shell expression" that can be used to match the hostname of the
' SSL server that is using this certificate. It is recommended that if
' the server's hostname does not match this pattern the user be notified
' and given the option to terminate the SSL connection. If this extension
' is not present then the CommonName in the certificate subject's
' distinguished name is used for the same purpose.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' szOID_NETSCAPE_COMMENT extension
'
' Its value is an IA5_STRING. CryptDecodeObject/CryptEncodeObject using
' X509_ANY_STRING or X509_UNICODE_ANY_STRING, where,
' dwValueType = CERT_RDN_IA5_STRING.
'
' It is a comment that may be displayed to the user when the certificate
' is viewed.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' szOID_NETSCAPE_CERT_SEQUENCE
'
' Its value is a PKCS#7 ContentInfo structure wrapping a sequence of
' certificates. The value of the contentType field is
' szOID_NETSCAPE_CERT_SEQUENCE, while the content field is the following
' structure:
' CertificateSequence ::= SEQUENCE OF Certificate.
'
' CryptDecodeObject/CryptEncodeObject using
' PKCS_CONTENT_INFO_SEQUENCE_OF_ANY, where,
' pszObjId = szOID_NETSCAPE_CERT_SEQUENCE and the CRYPT_DER_BLOBs point
' to encoded X509 certificates.
'--------------------------------------------------------------------------
'+=========================================================================
'==========================================================================
' Predefined OID Function Names
Public Const CRYPT_OID_ENCODE_OBJECT_FUNC As String = "CryptDllEncodeObject"
Public Const CRYPT_OID_DECODE_OBJECT_FUNC As String = "CryptDllDecodeObject"
Public Const CRYPT_OID_CREATE_COM_OBJECT_FUNC As String = "CryptDllCreateCOMObject"
Public Const CRYPT_OID_VERIFY_REVOCATION_FUNC As String = "CertDllVerifyRevocation"
Public Const CRYPT_OID_VERIFY_CTL_USAGE_FUNC As String = "CertDllVerifyCTLUsage"
Public Const CRYPT_OID_FORMAT_OBJECT_FUNC As String = "CryptDllFormatObject"
' CryptDllEncodeObject has same function signature as CryptEncodeObject.
' CryptDllDecodeObject has same function signature as CryptDecodeObject.
' CryptDllCreateCOMObject has the following signature:
' IN DWORD dwEncodingType,
' IN LPCSTR pszOID,
' IN PCRYPT_DATA_BLOB pEncodedContent,
' IN DWORD dwFlags,
' IN REFIID riid,
' OUT void **ppvObj);
' CertDllVerifyRevocation has the same signature as CertVerifyRevocation
' CertDllVerifyCTLUsage has the same signature as CertVerifyCTLUsage
' Example of a complete OID Function Registry Name:
' HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\OID
' Encoding Type 1\CryptDllEncodeObject\1.2.3
'
' The key's "Dll" value contains the name of the Dll.
' The key's "FuncName" value overrides the default function name
Public Const CRYPT_OID_REGPATH As String = "Software\\Microsoft\\Cryptography\\OID"
Public Const CRYPT_OID_REG_ENCODING_TYPE_PREFIX As String = "EncodingType "
Public Const CRYPT_OID_REG_DLL_VALUE_NAME As String = "Dll"
Public Const CRYPT_OID_REG_FUNC_NAME_VALUE_NAME As String = "FuncName"
Public Const CRYPT_OID_REG_FUNC_NAME_VALUE_NAME_A As String = "FuncName"
' OID used for Default OID functions
Public Const CRYPT_DEFAULT_OID As String = "DEFAULT"
Public Const CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG As Long = 1
'+-------------------------------------------------------------------------
' Install a set of callable OID function addresses.
'
' By default the functions are installed at end of the list.
' Set CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG to install at beginning of list.
'
' hModule should be updated with the hModule passed to DllMain to prevent
' the Dll containing the function addresses from being unloaded by
' CryptGetOIDFuncAddress/CryptFreeOIDFunctionAddress. This would be the
' case when the Dll has also regsvr32'ed OID functions via
' CryptRegisterOIDFunction.
'
' DEFAULT functions are installed by setting rgFuncEntry[].pszOID =
' CRYPT_DEFAULT_OID.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Initialize and return handle to the OID function set identified by its
' function name.
'
' If the set already exists, a handle to the existing set is returned.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Search the list of installed functions for an encoding type and OID match.
' If not found, search the registry.
'
' For success, returns TRUE with *ppvFuncAddr updated with the function's
' address and *phFuncAddr updated with the function address's handle.
' The function's handle is AddRef'ed. CryptFreeOIDFunctionAddress needs to
' be called to release it.
'
' For a registry match, the Dll containing the function is loaded.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Get the list of registered default Dll entries for the specified
' function set and encoding type.
'
' The returned list consists of none, one or more null terminated Dll file
' For example: "first.dll" L"\0" L"second.dll" L"\0" L"\0"
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Either: get the first or next installed DEFAULT function OR
' load the Dll containing the DEFAULT function.
'
' If pwszDll is NULL, search the list of installed DEFAULT functions.
' *phFuncAddr must be set to NULL to get the first installed function.
' Successive installed functions are returned by setting *phFuncAddr
' to the hFuncAddr returned by the previous call.
'
' If pwszDll is NULL, the input *phFuncAddr
' is always CryptFreeOIDFunctionAddress'ed by this function, even for
' an error.
'
' If pwszDll isn't NULL, then, attempts to load the Dll and the DEFAULT
' function. *phFuncAddr is ignored upon entry and isn't
' CryptFreeOIDFunctionAddress'ed.
'
' For success, returns TRUE with *ppvFuncAddr updated with the function's
' address and *phFuncAddr updated with the function address's handle.
' The function's handle is AddRef'ed. CryptFreeOIDFunctionAddress needs to
' be called to release it or CryptGetDefaultOIDFunctionAddress can also
' be called for a NULL pwszDll.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Releases the handle AddRef'ed and returned by CryptGetOIDFunctionAddress
' or CryptGetDefaultOIDFunctionAddress.
'
' If a Dll was loaded for the function its unloaded. However, before doing
' the unload, the DllCanUnloadNow function exported by the loaded Dll is
' called. It should return S_FALSE to inhibit the unload or S_TRUE to enable
' the unload. If the Dll doesn't export DllCanUnloadNow, the Dll is unloaded.
'
' DllCanUnloadNow has the following signature:
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Register the Dll containing the function to be called for the specified
' encoding type, function name and OID.
'
' pwszDll may contain environment-variable strings
'
' In addition to registering the DLL, you may override the
' name of the function to be called. For example,
' pszFuncName = "CryptDllEncodeObject",
' pszOverrideFuncName = "MyEncodeXyz".
' This allows a Dll to export multiple OID functions for the same
' function name without needing to interpose its own OID dispatcher function.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Unregister the Dll containing the function to be called for the specified
' encoding type, function name and OID.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Register the Dll containing the default function to be called for the
' specified encoding type and function name.
'
' Unlike CryptRegisterOIDFunction, you can't override the function name
' needing to be exported by the Dll.
'
' The Dll is inserted before the entry specified by dwIndex.
' dwIndex == 0, inserts at the beginning.
' dwIndex == CRYPT_REGISTER_LAST_INDEX, appends at the end.
'
' pwszDll may contain environment-variable strings
'--------------------------------------------------------------------------
Public Const CRYPT_REGISTER_FIRST_INDEX As Long = 0
Public Const CRYPT_REGISTER_LAST_INDEX As Long = &HFFFFFFFF
'+-------------------------------------------------------------------------
' Unregister the Dll containing the default function to be called for
' the specified encoding type and function name.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Set the value for the specified encoding type, function name, OID and
' value name.
'
' See RegSetValueEx for the possible value types.
'
' String types are UNICODE.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Get the value for the specified encoding type, function name, OID and
' value name.
'
' See RegEnumValue for the possible value types.
'
' String types are UNICODE.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Enumerate the OID functions identified by their encoding type,
' function name and OID.
'
' pfnEnumOIDFunc is called for each registry key matching the input
' parameters. Setting dwEncodingType to CRYPT_MATCH_ANY_ENCODING_TYPE matches
' any. Setting pszFuncName or pszOID to NULL matches any.
'
' Set pszOID == CRYPT_DEFAULT_OID to restrict the enumeration to only the
' DEFAULT functions
'
' String types are UNICODE.
'--------------------------------------------------------------------------
Public Const CRYPT_MATCH_ANY_ENCODING_TYPE As Long = &HFFFFFFFF
'+=========================================================================
' Low Level Cryptographic Message Data Structures and APIs
'==========================================================================
Public Const szOID_PKCS_7_DATA As String = "1.2.840.113549.1.7.1"
Public Const szOID_PKCS_7_SIGNED As String = "1.2.840.113549.1.7.2"
Public Const szOID_PKCS_7_ENVELOPED As String = "1.2.840.113549.1.7.3"
Public Const szOID_PKCS_7_SIGNEDANDENVELOPED As String = "1.2.840.113549.1.7.4"
Public Const szOID_PKCS_7_DIGESTED As String = "1.2.840.113549.1.7.5"
Public Const szOID_PKCS_7_ENCRYPTED As String = "1.2.840.113549.1.7.6"
Public Const szOID_PKCS_9_CONTENT_TYPE As String = "1.2.840.113549.1.9.3"
Public Const szOID_PKCS_9_MESSAGE_DIGEST As String = "1.2.840.113549.1.9.4"
'+-------------------------------------------------------------------------
' Message types
'--------------------------------------------------------------------------
Public Const CMSG_DATA As Long = 1
Public Const CMSG_SIGNED As Long = 2
Public Const CMSG_ENVELOPED As Long = 3
Public Const CMSG_SIGNED_AND_ENVELOPED As Long = 4
Public Const CMSG_HASHED As Long = 5
Public Const CMSG_ENCRYPTED As Long = 6
'+-------------------------------------------------------------------------
' Message Type Bit Flags
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_DATA: pvMsgEncodeInfo = NULL
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_SIGNED
'
' The pCertInfo in the CMSG_SIGNER_ENCODE_INFO provides the Issuer, SerialNumber
' and PublicKeyInfo.Algorithm. The PublicKeyInfo.Algorithm implicitly
' specifies the HashEncryptionAlgorithm to be used.
'
' The hCryptProv and dwKeySpec specify the private key to use. If dwKeySpec
' == 0, then, defaults to AT_SIGNATURE.
'
' pvHashAuxInfo currently isn't used and must be set to NULL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_ENVELOPED
'
' The PCERT_INFO for the rgRecipients provides the Issuer, SerialNumber
' and PublicKeyInfo. The PublicKeyInfo.Algorithm implicitly
' specifies the KeyEncryptionAlgorithm to be used.
'
' The PublicKeyInfo.PublicKey in PCERT_INFO is used to encrypt the content
' encryption key for the recipient.
'
' hCryptProv is used to do the content encryption, recipient key encryption
' and export. The hCryptProv's private keys aren't used.
'
' Note: CAPI currently doesn't support more than one KeyEncryptionAlgorithm
' per provider. This will need to be fixed.
'
' pvEncryptionAuxInfo currently isn't used and must be set to NULL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_SIGNED_AND_ENVELOPED
'
' For PKCS #7, a signed and enveloped message doesn't have the
' signer's authenticated or unauthenticated attributes. Otherwise, a
' combination of the CMSG_SIGNED_ENCODE_INFO and CMSG_ENVELOPED_ENCODE_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_HASHED
'
' hCryptProv is used to do the hash. Doesn't need to use a private key.
'
' If fDetachedHash is set, then, the encoded message doesn't contain
'
' pvHashAuxInfo currently isn't used and must be set to NULL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_ENCRYPTED
'
' The key used to encrypt the message is identified outside of the message
'
' The content input to CryptMsgUpdate has already been encrypted.
'
' pvEncryptionAuxInfo currently isn't used and must be set to NULL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' This parameter allows messages to be of variable length with streamed
' output.
'
' By default, messages are of a definite length and
' called to get the cryptographically processed content. Until closed,
' the handle keeps a copy of the processed content.
'
' With streamed output, the processed content can be freed as its streamed.
'
' If the length of the content to be updated is known at the time of the
' open, then, ContentLength should be set to that length. Otherwise, it
' should be set to CMSG_INDEFINITE_LENGTH.
'--------------------------------------------------------------------------
Public Const CMSG_INDEFINITE_LENGTH As Long = &HFFFFFFFF
'+-------------------------------------------------------------------------
' Open dwFlags
'--------------------------------------------------------------------------
Public Const CMSG_BARE_CONTENT_FLAG As Long = &H00000001
Public Const CMSG_LENGTH_ONLY_FLAG As Long = &H00000002
Public Const CMSG_DETACHED_FLAG As Long = &H00000004
Public Const CMSG_AUTHENTICATED_ATTRIBUTES_FLAG As Long = &H00000008
Public Const CMSG_CONTENTS_OCTETS_FLAG As Long = &H00000010
'+-------------------------------------------------------------------------
' Open a cryptographic message for encoding
'
' For PKCS #7:
' If the content to be passed to CryptMsgUpdate has already
' from another message encode), then, the CMSG_ENCODED_CONTENT_INFO_FLAG should
' be set in dwFlags. If not set, then, the inner ContentType is Data and
' the input to CryptMsgUpdate is treated as the inner Data type's Content,
' a string of bytes.
' If CMSG_BARE_CONTENT_FLAG is specified for a streamed message,
' the streamed output will not have an outer ContentInfo wrapper. This
' makes it suitable to be streamed into an enclosing message.
'
' The pStreamInfo parameter needs to be set to stream the encoded message
' output.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Calculate the length of an encoded cryptographic message.
'
' Calculates the length of the encoded message given the
' message type, encoding parameters and total length of
' the data to be updated. Note, this might not be the exact length. However,
' it will always be greater than or equal to the actual length.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Open a cryptographic message for decoding
'
' BUGBUG! These comments need to be changed
' For PKCS #7: if the inner ContentType isn't Data, then, the inner
' ContentInfo consisting of both ContentType and Content is output.
' To also enable ContentInfo output for the Data ContentType, then,
' the CMSG_ENCODED_CONTENT_INFO_FLAG should be set
' in dwFlags. If not set, then, only the content portion of the inner
' ContentInfo is output for the Data ContentType.
'
' To only calculate the length of the decoded message, set the
' CMSG_LENGTH_ONLY_FLAG in dwFlags. After the final CryptMsgUpdate get the
' MSG_CONTENT_PARAM. Note, this might not be the exact length. However,
' it will always be greater than or equal to the actual length.
'
' hCryptProv specifies the crypto provider to use for hashing and/or
' decrypting the message. For enveloped messages, hCryptProv also specifies
' the private exchange key to use. For signed messages, hCryptProv is used
' when CryptMsgVerifySigner is called.
'
' For enveloped messages, the pRecipientInfo contains the Issuer and
' SerialNumber identifying the RecipientInfo in the message.
'
' Note, the pRecipientInfo should correspond to the provider's private
' exchange key.
'
' If pRecipientInfo is NULL, then, the message isn't decrypted. To decrypt
' CryptMsgUpdate.
'
' The pStreamInfo parameter needs to be set to stream the decoded content
' output. Note, if pRecipientInfo is NULL, then, the streamed output isn't
' decrypted.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Close a cryptographic message handle
'
' LastError is preserved unless FALSE is returned.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Update the content of a cryptographic message. Depending on how the
' message was opened, the content is either encoded or decoded.
'
' This function is repetitively called to append to the message content.
' fFinal is set to identify the last update. On fFinal, the encode/decode
' is completed. The encoded/decoded content and the decoded parameters
' are valid until the open and all duplicated handles are closed.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Perform a special "control" function after the final CryptMsgUpdate of a
' encoded/decoded cryptographic message.
'
' The dwCtrlType parameter specifies the type of operation to be performed.
'
' The pvCtrlPara definition depends on the dwCtrlType value.
'
' See below for a list of the control operations and their pvCtrlPara
' type definition.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Message control types
'--------------------------------------------------------------------------
Public Const CMSG_CTRL_VERIFY_SIGNATURE As Long = 1
Public Const CMSG_CTRL_DECRYPT As Long = 2
Public Const CMSG_CTRL_VERIFY_HASH As Long = 5
Public Const CMSG_CTRL_ADD_SIGNER As Long = 6
Public Const CMSG_CTRL_DEL_SIGNER As Long = 7
Public Const CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR As Long = 8
Public Const CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR As Long = 9
Public Const CMSG_CTRL_ADD_CERT As Long = 10
Public Const CMSG_CTRL_DEL_CERT As Long = 11
Public Const CMSG_CTRL_ADD_CRL As Long = 12
Public Const CMSG_CTRL_DEL_CRL As Long = 13
'+-------------------------------------------------------------------------
' CMSG_CTRL_VERIFY_SIGNATURE
'
' Verify the signature of a SIGNED or SIGNED_AND_ENVELOPED
' message after it has been decoded.
'
' For a SIGNED_AND_ENVELOPED message, called after
' with a NULL pRecipientInfo.
'
' pvCtrlPara points to a CERT_INFO struct.
'
' The CERT_INFO contains the Issuer and SerialNumber identifying
' the Signer of the message. The CERT_INFO also contains the
' PublicKeyInfo
' used to verify the signature. The cryptographic provider specified
' in CryptMsgOpenToDecode is used.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CTRL_DECRYPT
'
' Decrypt an ENVELOPED or SIGNED_AND_ENVELOPED message after it has been
' decoded.
'
' hCryptProv and dwKeySpec specify the private key to use. For dwKeySpec ==
' 0, defaults to AT_KEYEXCHANGE.
'
' dwRecipientIndex is the index of the recipient in the message associated
' with the hCryptProv's private key.
'
' This control function needs to be called, if you don't know the appropriate
' recipient before calling CryptMsgOpenToDecode. After the final
' CryptMsgUpdate, the list of recipients is obtained by iterating through
' CMSG_RECIPIENT_INFO_PARAM. The recipient corresponding to a private
' key owned by the caller is selected and passed to this function to decrypt
' the message.
'
' Note, the message can only be decrypted once.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CTRL_VERIFY_HASH
'
' Verify the hash of a HASHED message after it has been decoded.
'
' Only the hCryptMsg parameter is used, to specify the message whose
' hash is being verified.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CTRL_ADD_SIGNER
'
' Add a signer to a signed-data or signed-and-enveloped-data message.
'
' pvCtrlPara points to a CMSG_SIGNER_ENCODE_INFO.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CTRL_DEL_SIGNER
'
' Remove a signer from a signed-data or signed-and-enveloped-data message.
'
' pvCtrlPara points to a DWORD containing the 0-based index of the
' signer to be removed.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR
'
' Add an unauthenticated attribute to the SignerInfo of a signed-data or
' signed-and-enveloped-data message.
'
' The unauthenticated attribute is input in the form of an encoded blob.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR
'
' Delete an unauthenticated attribute from the SignerInfo of a signed-data
' or signed-and-enveloped-data message.
'
' The unauthenticated attribute to be removed is specified by
' a 0-based index.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CTRL_ADD_CERT
'
' Add a certificate to a signed-data or signed-and-enveloped-data message.
'
' pvCtrlPara points to a CRYPT_DATA_BLOB containing the certificate's
' encoded bytes.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CTRL_DEL_CERT
'
' Delete a certificate from a signed-data or signed-and-enveloped-data
' message.
'
' pvCtrlPara points to a DWORD containing the 0-based index of the
' certificate to be removed.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CTRL_ADD_CRL
'
' Add a CRL to a signed-data or signed-and-enveloped-data message.
'
' pvCtrlPara points to a CRYPT_DATA_BLOB containing the CRL's
' encoded bytes.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CTRL_DEL_CRL
'
' Delete a CRL from a signed-data or signed-and-enveloped-data message.
'
' pvCtrlPara points to a DWORD containing the 0-based index of the CRL
' to be removed.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verify a countersignature, at the SignerInfo level.
' ie. verify that pbSignerInfoCountersignature contains the encrypted
' hash of the encryptedDigest field of pbSignerInfo.
'
' hCryptProv is used to hash the encryptedDigest field of pbSignerInfo.
' The only fields referenced from pciCountersigner are SerialNumber, Issuer,
' and SubjectPublicKeyInfo.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Countersign an already-existing signature in a message
'
' dwIndex is a zero-based index of the SignerInfo to be countersigned.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Output an encoded SignerInfo blob, suitable for use as a countersignature
' attribute in the unauthenticated attributes of a signed-data or
' signed-and-enveloped-data message.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Get a parameter after encoding/decoding a cryptographic message. Called
' after the final CryptMsgUpdate. Only the CMSG_CONTENT_PARAM and
' CMSG_COMPUTED_HASH_PARAM are valid for an encoded message.
'
' For an encoded HASHED message, the CMSG_COMPUTED_HASH_PARAM can be got
' before any CryptMsgUpdates to get its length.
'
' The pvData type definition depends on the dwParamType value.
'
' Elements pointed to by fields in the pvData structure follow the
' structure. Therefore, *pcbData may exceed the size of the structure.
'
' Upon input, if *pcbData == 0, then, *pcbData is updated with the length
' of the data and the pvData parameter is ignored.
'
' Upon return, *pcbData is updated with the length of the data.
'
' The OBJID BLOBs returned in the pvData structures point to
' their still encoded representation. The appropriate functions
' must be called to decode the information.
'
' See below for a list of the parameters to get.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Get parameter types and their corresponding data structure definitions.
'--------------------------------------------------------------------------
Public Const CMSG_TYPE_PARAM As Long = 1
Public Const CMSG_CONTENT_PARAM As Long = 2
Public Const CMSG_BARE_CONTENT_PARAM As Long = 3
Public Const CMSG_INNER_CONTENT_TYPE_PARAM As Long = 4
Public Const CMSG_SIGNER_COUNT_PARAM As Long = 5
Public Const CMSG_SIGNER_INFO_PARAM As Long = 6
Public Const CMSG_SIGNER_CERT_INFO_PARAM As Long = 7
Public Const CMSG_SIGNER_HASH_ALGORITHM_PARAM As Long = 8
Public Const CMSG_SIGNER_AUTH_ATTR_PARAM As Long = 9
Public Const CMSG_SIGNER_UNAUTH_ATTR_PARAM As Long = 10
Public Const CMSG_CERT_COUNT_PARAM As Long = 11
Public Const CMSG_CERT_PARAM As Long = 12
Public Const CMSG_CRL_COUNT_PARAM As Long = 13
Public Const CMSG_CRL_PARAM As Long = 14
Public Const CMSG_ENVELOPE_ALGORITHM_PARAM As Long = 15
Public Const CMSG_RECIPIENT_COUNT_PARAM As Long = 17
Public Const CMSG_RECIPIENT_INDEX_PARAM As Long = 18
Public Const CMSG_RECIPIENT_INFO_PARAM As Long = 19
Public Const CMSG_HASH_ALGORITHM_PARAM As Long = 20
Public Const CMSG_HASH_DATA_PARAM As Long = 21
Public Const CMSG_COMPUTED_HASH_PARAM As Long = 22
Public Const CMSG_ENCRYPT_PARAM As Long = 26
Public Const CMSG_ENCRYPTED_DIGEST As Long = 27
Public Const CMSG_ENCODED_SIGNER As Long = 28
Public Const CMSG_ENCODED_MESSAGE As Long = 29
'+-------------------------------------------------------------------------
' CMSG_TYPE_PARAM
'
' The type of the decoded message.
'
' pvData points to a DWORD

'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CONTENT_PARAM
'
' The encoded content of a cryptographic message. Depending on how the
' message was opened, the content is either the whole PKCS#7
' In the decode case, the decrypted content is returned, if enveloped.
' If not enveloped, and if the inner content is of type DATA, the returned
' data is the contents octets of the inner content.
'
' pvData points to the buffer receiving the content bytes
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_BARE_CONTENT_PARAM
'
' The encoded content of an encoded cryptographic message, without the
' outer layer of ContentInfo. That is, only the encoding of the
' ContentInfo.content field is returned.
'
' pvData points to the buffer receiving the content bytes
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_INNER_CONTENT_TYPE_PARAM
'
' The type of the inner content of a decoded cryptographic message,
' in the form of a NULL-terminated object identifier string
'
' pvData points to the buffer receiving the object identifier string
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_SIGNER_COUNT_PARAM
'
' Count of signers in a SIGNED or SIGNED_AND_ENVELOPED message
'
' pvData points to a DWORD
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_SIGNER_CERT_INFO_PARAM
'
' To get all the signers, repetitively call CryptMsgGetParam, with
' dwIndex set to 0 .. SignerCount - 1.
'
' pvData points to a CERT_INFO struct.
'
' Only the following fields have been updated in the CERT_INFO struct:
' Issuer and SerialNumber.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_SIGNER_INFO_PARAM
'
' To get all the signers, repetitively call CryptMsgGetParam, with
' dwIndex set to 0 .. SignerCount - 1.
'
' pvData points to a CMSG_SIGNER_INFO struct.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_SIGNER_HASH_ALGORITHM_PARAM
'
' This parameter specifies the HashAlgorithm that was used for the signer.
'
' Set dwIndex to iterate through all the signers.
'
' pvData points to an CRYPT_ALGORITHM_IDENTIFIER struct.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_SIGNER_AUTH_ATTR_PARAM
'
' The authenticated attributes for the signer.
'
' Set dwIndex to iterate through all the signers.
'
' pvData points to a CMSG_ATTR struct.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_SIGNER_UNAUTH_ATTR_PARAM
'
' The unauthenticated attributes for the signer.
'
' Set dwIndex to iterate through all the signers.
'
' pvData points to a CMSG_ATTR struct.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CERT_COUNT_PARAM
'
' Count of certificates in a SIGNED or SIGNED_AND_ENVELOPED message.
'
' pvData points to a DWORD
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CERT_PARAM
'
' To get all the certificates, repetitively call CryptMsgGetParam, with
' dwIndex set to 0 .. CertCount - 1.
'
' pvData points to an array of the certificate's encoded bytes.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CRL_COUNT_PARAM
'
' Count of CRLs in a SIGNED or SIGNED_AND_ENVELOPED message.
'
' pvData points to a DWORD
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_CRL_PARAM
'
' To get all the CRLs, repetitively call CryptMsgGetParam, with
' dwIndex set to 0 .. CrlCount - 1.
'
' pvData points to an array of the CRL's encoded bytes.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_ENVELOPE_ALGORITHM_PARAM
'
' The ContentEncryptionAlgorithm that was used in
' an ENVELOPED or SIGNED_AND_ENVELOPED message.
'
' pvData points to an CRYPT_ALGORITHM_IDENTIFIER struct.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_RECIPIENT_COUNT_PARAM
'
' Count of recipients in an ENVELOPED or SIGNED_AND_ENVELOPED message.
'
' pvData points to a DWORD
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_RECIPIENT_INDEX_PARAM
'
' Index of the recipient used to decrypt an ENVELOPED or SIGNED_AND_ENVELOPED
' message.
'
' pvData points to a DWORD
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_RECIPIENT_INFO_PARAM
'
' To get all the recipients, repetitively call CryptMsgGetParam, with
' dwIndex set to 0 .. RecipientCount - 1.
'
' pvData points to a CERT_INFO struct.
'
' Only the following fields have been updated in the CERT_INFO struct:
' Issuer, SerialNumber and PublicKeyAlgorithm. The PublicKeyAlgorithm
' specifies the KeyEncryptionAlgorithm that was used.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_HASH_ALGORITHM_PARAM
'
' The HashAlgorithm in a HASHED message.
'
' pvData points to an CRYPT_ALGORITHM_IDENTIFIER struct.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_HASH_DATA_PARAM
'
' The hash in a HASHED message.
'
' pvData points to an array of bytes.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_COMPUTED_HASH_PARAM
'
' The computed hash for a HASHED message.
'
' This may be called for either an encoded or decoded message.
' It also may be called before any encoded CryptMsgUpdates to get its length.
'
' pvData points to an array of bytes.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_ENCRYPT_PARAM
'
' The ContentEncryptionAlgorithm that was used in an ENCRYPTED message.
'
' pvData points to an CRYPT_ALGORITHM_IDENTIFIER struct.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CMSG_ENCODED_MESSAGE
'
' The full encoded message. This is useful in the case of a decoded
' signed-and-enveloped-data message which has been countersigned).
'
' pvData points to an array of the message's encoded bytes.
'--------------------------------------------------------------------------
'+=========================================================================
' Certificate Store Data Structures and APIs
'==========================================================================
'+-------------------------------------------------------------------------
' In its most basic implementation, a cert store is simply a
' collection of certificates and/or CRLs. This is the case when
' a cert store is opened with all of its certificates and CRLs
' coming from a PKCS #7 encoded cryptographic message.
'
' Nonetheless, all cert stores have the following properties:
' - A public key may have more than one certificate in the store.
' For example, a private/public key used for signing may have a
' certificate issued for VISA and another issued for
' Mastercard. Also, when a certificate is renewed there might
' be more than one certificate with the same subject and
' issuer.
' - However, each certificate in the store is uniquely
' identified by its Issuer and SerialNumber.
' - There's an issuer of subject certificate relationship. A
' certificate's issuer is found by doing a match of
' pSubjectCert->Issuer with pIssuerCert->Subject.
' The relationship is verified by using
' the issuer's public key to verify the subject certificate's
' signature. Note, there might be X.509 v3 extensions
' to assist in finding the issuer certificate.
' - Since issuer certificates might be renewed, a subject
' certificate might have more than one issuer certificate.
' - There's an issuer of CRL relationship. An
' issuer's CRL is found by doing a match of
' pIssuerCert->Subject with pCrl->Issuer.
' The relationship is verified by using
' the issuer's public key to verify the CRL's
' signature. Note, there might be X.509 v3 extensions
' to assist in finding the CRL.
' - Since some issuers might support the X.509 v3 delta CRL
' extensions, an issuer might have more than one CRL.
' - The store shouldn't have any redundant certificates or
' CRLs. There shouldn't be two certificates with the same
' Issuer and SerialNumber. There shouldn't be two CRLs with
' the same Issuer, ThisUpdate and NextUpdate.
' - The store has NO policy or trust information. No
' certificates are tagged as being "root". Its up to
' SerialNumber) for certificates it trusts.
' - The store might contain bad certificates and/or CRLs.
' The issuer's signature of a subject certificate or CRL may
' not verify. Certificates or CRLs may not satisfy their
' time validity requirements. Certificates may be
' revoked.
'
' In addition to the certificates and CRLs, properties can be
' stored. There are two predefined property IDs for a user
' certificate: CERT_KEY_PROV_HANDLE_PROP_ID and
' CERT_KEY_PROV_INFO_PROP_ID. The CERT_KEY_PROV_HANDLE_PROP_ID
' is a HCRYPTPROV handle to the private key assoicated
' with the certificate. The CERT_KEY_PROV_INFO_PROP_ID contains
' information to be used to call
' CryptAcquireContext and CryptProvSetParam to get a handle
' to the private key associated with the certificate.
'
' There exists two more predefined property IDs for certificates
' and CRLs, CERT_SHA1_HASH_PROP_ID and CERT_MD5_HASH_PROP_ID.
' If these properties don't already exist, then, a hash of the
' hash algorithm, currently, CERT_SHA1_HASH_PROP_ID).
'
' There are additional APIs for creating certificate and CRL
' CertCreateCRLContext).
'
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate context.
'
' A certificate context contains both the encoded and decoded representation
' of a certificate. A certificate context returned by a cert store function
' must be freed by calling the CertFreeCertificateContext function. The
' CertDuplicateCertificateContext function can be called to make a duplicate
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CRL context.
'
' A CRL context contains both the encoded and decoded representation
' of a CRL. A CRL context returned by a cert store function
' must be freed by calling the CertFreeCRLContext function. The
' CertDuplicateCRLContext function can be called to make a duplicate
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
'
' A CTL context contains both the encoded and decoded representation
' of a CTL. Also contains an opened HCRYPTMSG handle to the decoded
' cryptographic signed message containing the CTL_INFO as its inner content.
' pbCtlContent is the encoded inner content of the signed message.
'
' The CryptMsg APIs can be used to extract additional signer information.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate, CRL and CTL property IDs
'
' See CertSetCertificateContextProperty or CertGetCertificateContextProperty
' for usage information.
'--------------------------------------------------------------------------
Public Const CERT_KEY_PROV_HANDLE_PROP_ID As Long = 1
Public Const CERT_KEY_PROV_INFO_PROP_ID As Long = 2
Public Const CERT_SHA1_HASH_PROP_ID As Long = 3
Public Const CERT_MD5_HASH_PROP_ID As Long = 4
Public Const CERT_KEY_CONTEXT_PROP_ID As Long = 5
Public Const CERT_KEY_SPEC_PROP_ID As Long = 6
Public Const CERT_IE30_RESERVED_PROP_ID As Long = 7
Public Const CERT_PUBKEY_HASH_RESERVED_PROP_ID As Long = 8
Public Const CERT_ENHKEY_USAGE_PROP_ID As Long = 9
Public Const CERT_NEXT_UPDATE_LOCATION_PROP_ID As Long = 10
Public Const CERT_FRIENDLY_NAME_PROP_ID As Long = 11
' Note, 32 - 34 are reserved for the CERT, CRL and CTL file element IDs.
Public Const CERT_FIRST_RESERVED_PROP_ID As Long = 12
Public Const CERT_LAST_RESERVED_PROP_ID As Long = &H00007FFF
Public Const CERT_FIRST_USER_PROP_ID As Long = &H00008000
Public Const CERT_LAST_USER_PROP_ID As Long = &H0000FFFF
'+-------------------------------------------------------------------------
' Cryptographic Key Provider Information
'
' CRYPT_KEY_PROV_INFO defines the CERT_KEY_PROV_INFO_PROP_ID's pvData.
'
' The CRYPT_KEY_PROV_INFO fields are passed to CryptAcquireContext
' to get a HCRYPTPROV handle. The optional CRYPT_KEY_PROV_PARAM fields are
' passed to CryptProvSetParam to further initialize the provider.
'
' The dwKeySpec field identifies the private key to use from the container
' For example, AT_KEYEXCHANGE or AT_SIGNATURE.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' The following flag should be set in the above dwFlags to enable
' CryptAcquireContext is done in the Sign or Decrypt Message functions.
'
' The following define must not collide with any of the
' CryptAcquireContext dwFlag defines.
'--------------------------------------------------------------------------
Public Const CERT_SET_KEY_PROV_HANDLE_PROP_ID As Long = &H00000001
Public Const CERT_SET_KEY_CONTEXT_PROP_ID As Long = &H00000001
'+-------------------------------------------------------------------------
' Certificate Key Context
'
' CERT_KEY_CONTEXT defines the CERT_KEY_CONTEXT_PROP_ID's pvData.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate Store Provider Types
'--------------------------------------------------------------------------
Public Const sz_CERT_STORE_PROV_MEMORY As String = "Memory"
Public Const sz_CERT_STORE_PROV_FILENAME_W As String = "File"
Public Const sz_CERT_STORE_PROV_SYSTEM_W As String = "System"
Public Const sz_CERT_STORE_PROV_PKCS7 As String = "PKCS7"
Public Const sz_CERT_STORE_PROV_SERIALIZED As String = "Serialized"
'+-------------------------------------------------------------------------
' Certificate Store verify/results flags
'--------------------------------------------------------------------------
Public Const CERT_STORE_SIGNATURE_FLAG As Long = &H00000001
Public Const CERT_STORE_TIME_VALIDITY_FLAG As Long = &H00000002
Public Const CERT_STORE_REVOCATION_FLAG As Long = &H00000004
Public Const CERT_STORE_NO_CRL_FLAG As Long = &H00010000
Public Const CERT_STORE_NO_ISSUER_FLAG As Long = &H00020000
'+-------------------------------------------------------------------------
' Certificate Store open/property flags
'--------------------------------------------------------------------------
Public Const CERT_STORE_NO_CRYPT_RELEASE_FLAG As Long = &H00000001
Public Const CERT_STORE_READONLY_FLAG As Long = &H00008000
'+-------------------------------------------------------------------------
' Certificate Store Provider flags are in the HiWord 0xFFFF0000
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate System Store Flag Values
'--------------------------------------------------------------------------
' Location of the system store in the registry:
' HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE
Public Const CERT_SYSTEM_STORE_LOCATION_MASK As Long = &H00030000
Public Const CERT_SYSTEM_STORE_CURRENT_USER As Long = &H00010000
Public Const CERT_SYSTEM_STORE_LOCAL_MACHINE As Long = &H00020000
'+-------------------------------------------------------------------------
' Open the cert store using the specified store provider.
'
' hCryptProv specifies the crypto provider to use to create the hash
' properties or verify the signature of a subject certificate or CRL.
' The store doesn't need to use a private
' key. If the CERT_STORE_NO_CRYPT_RELEASE_FLAG isn't set, hCryptProv is
' CryptReleaseContext'ed on the final CertCloseStore.
'
' Note, if the open fails, hCryptProv is released if it would have been
' released when the store was closed.
'
' If hCryptProv is zero, then, the default provider and container for the
' PROV_RSA_FULL provider type is CryptAcquireContext'ed with
' CRYPT_VERIFYCONTEXT access. The CryptAcquireContext is deferred until
' the first create hash or verify signature. In addition, once acquired,
' the default provider isn't released until process exit when crypt32.dll
' is unloaded. The acquired default provider is shared across all stores
' and threads.
'
' After initializing the store's data structures and optionally acquiring a
' default crypt provider, CertOpenStore calls CryptGetOIDFunctionAddress to
' get the address of the CRYPT_OID_OPEN_STORE_PROV_FUNC specified by
' lpszStoreProvider. Since a store can contain certificates with different
' encoding types, CryptGetOIDFunctionAddress is called with dwEncodingType
' set to 0 and not the dwEncodingType passed to CertOpenStore.
' PFN_CERT_DLL_OPEN_STORE_FUNC specifies the signature of the provider's
' open function. This provider open function is called to load the
' store's certificates and CRLs. Optionally, the provider may return an
' array of functions called before a certificate or CRL is added or deleted
' or has a property that is set.
'
' Use of the dwEncodingType parameter is provider dependent. The type
' definition for pvPara also depends on the provider.
'
' Store providers are installed or registered via
' CryptInstallOIDFunctionAddress or CryptRegisterOIDFunction, where,
' dwEncodingType is 0 and pszFuncName is CRYPT_OID_OPEN_STORE_PROV_FUNC.
'
'
' CERT_STORE_PROV_MSG:
' Gets the certificates and CRLs from the specified cryptographic message.
' dwEncodingType contains the message and certificate encoding types.
' The message's handle is passed in pvPara. Given,
'
' CERT_STORE_PROV_MEMORY
' sz_CERT_STORE_PROV_MEMORY:
' Opens a store without any initial certificates or CRLs. pvPara
' isn't used.
'
' CERT_STORE_PROV_FILE:
' Reads the certificates and CRLs from the specified file. The file's
' handle is passed in pvPara. Given,
'
' For a successful open, the file pointer is advanced past
' the certificates and CRLs and their properties read from the file.
' Note, only expects a serialized store and not a file containing
' either a PKCS #7 signed message or a single encoded certificate.
'
' The hFile isn't closed.
'
' CERT_STORE_PROV_REG:
' Reads the certificates and CRLs from the registry. The registry's
' key handle is passed in pvPara. Given,
'
' The input hKey isn't closed by the provider. Before returning, the
' provider opens/creates "Certificates" and "CRLs" subkeys. These
' subkeys remain open until the store is closed.
'
' If CERT_STORE_READONLY_FLAG is set, then, the registry subkeys are
' RegOpenKey'ed with KEY_READ_ACCESS. Otherwise, the registry subkeys
' are RegCreateKey'ed with KEY_ALL_ACCESS.
'
' This provider returns the array of functions for reading, writing,
' deleting and property setting certificates and CRLs.
' Any changes to the opened store are immediately pushed through to
' the registry. However, if CERT_STORE_READONLY_FLAG is set, then,
' writing, deleting or property setting results in a
'
' Note, all the certificates and CRLs are read from the registry
' when the store is opened. The opened store serves as a write through
' cache. However, the opened store isn't notified of other changes
' made to the registry. Note, RegNotifyChangeKeyValue is supported
' on NT but not supported on Windows95.
'
' CERT_STORE_PROV_PKCS7:
' sz_CERT_STORE_PROV_PKCS7:
' Gets the certificates and CRLs from the encoded PKCS #7 signed message.
' dwEncodingType specifies the message and certificate encoding types.
' The pointer to the encoded message's blob is passed in pvPara. Given,
'
' Note, also supports the IE3.0 special version of a
' PKCS #7 signed message referred to as a "SPC" formatted message.
'
' CERT_STORE_PROV_SERIALIZED:
' sz_CERT_STORE_PROV_SERIALIZED:
' Gets the certificates and CRLs from memory containing a serialized
' store. The pointer to the serialized memory blob is passed in pvPara.
' Given,
'
' CERT_STORE_PROV_FILENAME_A:
' CERT_STORE_PROV_FILENAME_W:
' CERT_STORE_PROV_FILENAME:
' sz_CERT_STORE_PROV_FILENAME_W:
' sz_CERT_STORE_PROV_FILENAME:
' Opens the file and first attempts to read as a serialized store. Then,
' as a PKCS #7 signed message. Finally, as a single encoded certificate.
' The filename is passed in pvPara. The filename is UNICODE for the
' "_W" provider and ASCII for the "_A" provider. For "_W": given,
' For "_A": given,
'
'
' Note, also supports the reading of the IE3.0 special version of a
' PKCS #7 signed message file referred to as a "SPC" formatted file.
'
' CERT_STORE_PROV_SYSTEM_A:
' CERT_STORE_PROV_SYSTEM_W:
' CERT_STORE_PROV_SYSTEM:
' sz_CERT_STORE_PROV_SYSTEM_W:
' sz_CERT_STORE_PROV_SYSTEM:
' Opens the specified "system" store. Currently, all the system
' stores are stored in the registry. The upper word of the dwFlags
' parameter is used to specify the location of the system store. It
' should be set to either CERT_SYSTEM_STORE_CURRENT_USER for
' HKEY_CURRENT_USER or CERT_SYSTEM_STORE_LOCAL_MACHINE for
' HKEY_LOCAL_MACHINE.
'
' After opening the registry key associated with the system name,
' the CERT_STORE_PROV_REG provider is called to complete the open.
'
' The system store name is passed in pvPara. The name is UNICODE for the
' "_W" provider and ASCII for the "_A" provider. For "_W": given,
' For "_A": given,
'
'
' If CERT_STORE_READONLY_FLAG is set, then, the registry is
' RegOpenKey'ed with KEY_READ_ACCESS. Otherwise, the registry is
' RegCreateKey'ed with KEY_ALL_ACCESS.
'
' The "root" store is treated differently from the other system
' stores. Before a certificate is added to or deleted from the "root"
' store, a pop up message box is displayed. The certificate's subject,
' issuer, serial number, time validity, sha1 and md5 thumbprints are
' displayed. The user is given the option to do the add or delete.
' If they don't allow the operation, LastError is set to E_ACCESSDENIED.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' OID Installable Certificate Store Provider Data Structures
'--------------------------------------------------------------------------
' Handle returned by the store provider when opened.
' Store Provider OID function's pszFuncName.
Public Const CRYPT_OID_OPEN_STORE_PROV_FUNC As String = "CertDllOpenStoreProv"
' Note, the Store Provider OID function's dwEncodingType is always 0.
' The following information is returned by the provider when opened. Its
' zeroed with cbSize set before the provider is called. If the provider
' doesn't need to be called again after the open it doesn't need to
' make any updates to the CERT_STORE_PROV_INFO.
' Definition of the store provider's open function.
'
' *pStoreProvInfo has been zeroed before the call.
'
' Note, pStoreProvInfo->cStoreProvFunc should be set last. Once set,
' all subsequent store calls, such as CertAddSerializedElementToStore will
' call the appropriate provider callback function.
' Indices into the store provider's array of callback functions.
'
' The provider can implement any subset of the following functions. It
' sets pStoreProvInfo->cStoreProvFunc to the last index + 1 and any
' preceding not implemented functions to NULL.
Public Const CERT_STORE_PROV_CLOSE_FUNC As Long = 0
Public Const CERT_STORE_PROV_READ_CERT_FUNC As Long = 1
Public Const CERT_STORE_PROV_WRITE_CERT_FUNC As Long = 2
Public Const CERT_STORE_PROV_DELETE_CERT_FUNC As Long = 3
Public Const CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC As Long = 4
Public Const CERT_STORE_PROV_READ_CRL_FUNC As Long = 5
Public Const CERT_STORE_PROV_WRITE_CRL_FUNC As Long = 6
Public Const CERT_STORE_PROV_DELETE_CRL_FUNC As Long = 7
Public Const CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC As Long = 8
Public Const CERT_STORE_PROV_READ_CTL_FUNC As Long = 9
Public Const CERT_STORE_PROV_WRITE_CTL_FUNC As Long = 10
Public Const CERT_STORE_PROV_DELETE_CTL_FUNC As Long = 11
Public Const CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC As Long = 12
' Called by CertCloseStore when the store's reference count is
' decremented to 0.
' Currently not called directly by the store APIs. However, may be exported
' to support other providers based on it.
'
' Reads the provider's copy of the certificate context. If it exists,
' creates a new certificate context.
Public Const CERT_STORE_PROV_WRITE_ADD_FLAG As Long = &H1
' Called by CertAddEncodedCertificateToStore,
' CertAddCertificateContextToStore or CertAddSerializedElementToStore before
' adding to the store. The CERT_STORE_PROV_WRITE_ADD_FLAG is set. In
' addition to the encoded certificate, the added pCertContext might also
' have properties.
'
' Returns TRUE if its OK to update the the store.
' Called by CertDeleteCertificateFromStore before deleting from the
' store.
'
' Returns TRUE if its OK to delete from the store.
' Called by CertSetCertificateContextProperty before setting the
' certificate's property. Also called by CertGetCertificateContextProperty,
' when getting a hash property that needs to be created and then persisted
' via the set.
'
' Upon input, the property hasn't been set for the pCertContext parameter.
'
' Returns TRUE if its OK to set the property.
' Currently not called directly by the store APIs. However, may be exported
' to support other providers based on it.
'
' Reads the provider's copy of the CRL context. If it exists,
' creates a new CRL context.
' Called by CertAddEncodedCRLToStore,
' CertAddCRLContextToStore or CertAddSerializedElementToStore before
' adding to the store. The CERT_STORE_PROV_WRITE_ADD_FLAG is set. In
' addition to the encoded CRL, the added pCertContext might also
' have properties.
'
' Returns TRUE if its OK to update the the store.
' Called by CertDeleteCRLFromStore before deleting from the store.
'
' Returns TRUE if its OK to delete from the store.
' Called by CertSetCRLContextProperty before setting the
' CRL's property. Also called by CertGetCRLContextProperty,
' when getting a hash property that needs to be created and then persisted
' via the set.
'
' Upon input, the property hasn't been set for the pCrlContext parameter.
'
' Returns TRUE if its OK to set the property.
' Currently not called directly by the store APIs. However, may be exported
' to support other providers based on it.
'
' Reads the provider's copy of the CTL context. If it exists,
' creates a new CTL context.
' Called by CertAddEncodedCTLToStore,
' CertAddCTLContextToStore or CertAddSerializedElementToStore before
' adding to the store. The CERT_STORE_PROV_WRITE_ADD_FLAG is set. In
' addition to the encoded CTL, the added pCertContext might also
' have properties.
'
' Returns TRUE if its OK to update the the store.
' Called by CertDeleteCTLFromStore before deleting from the store.
'
' Returns TRUE if its OK to delete from the store.
' Called by CertSetCTLContextProperty before setting the
' CTL's property. Also called by CertGetCTLContextProperty,
' when getting a hash property that needs to be created and then persisted
' via the set.
'
' Upon input, the property hasn't been set for the pCtlContext parameter.
'
' Returns TRUE if its OK to set the property.
'+-------------------------------------------------------------------------
' Duplicate a cert store handle
'--------------------------------------------------------------------------
Public Const CERT_STORE_SAVE_AS_STORE As Long = 1
Public Const CERT_STORE_SAVE_AS_PKCS7 As Long = 2
Public Const CERT_STORE_SAVE_TO_FILE As Long = 1
Public Const CERT_STORE_SAVE_TO_MEMORY As Long = 2
Public Const CERT_STORE_SAVE_TO_FILENAME_A As Long = 3
Public Const CERT_STORE_SAVE_TO_FILENAME_W As Long = 4
'+-------------------------------------------------------------------------

' Save the cert store. Extended version with lots of options.
'
' According to the dwSaveAs parameter, the store can be saved as a
' addition to encoded certificates, CRLs and CTLs or the store can be saved
' include the properties or CTLs.
'
' CERT_KEY_PROV_HANDLE_PROP_ID or CERT_KEY_SPEC_PROP_ID) isn't saved into
' a serialized store.
'
' For CERT_STORE_SAVE_AS_PKCS7, the dwEncodingType specifies the message
' encoding type. The dwEncodingType parameter isn't used for
' CERT_STORE_SAVE_AS_STORE.
'
' The dwFlags parameter currently isn't used and should be set to 0.
'
' The dwSaveTo and pvSaveToPara parameters specify where to save the
' store as follows:
' CERT_STORE_SAVE_TO_FILE:
' Saves to the specified file. The file's handle is passed in
' pvSaveToPara. Given,
'
' For a successful save, the file pointer is positioned after the
' last write.
'
' CERT_STORE_SAVE_TO_MEMORY:
' Saves to the specified memory blob. The pointer to
' the memory blob is passed in pvSaveToPara. Given,
' Upon entry, the SaveBlob's pbData and cbData need to be initialized.
' Upon return, cbData is updated with the actual length.
' For a length only calculation, pbData should be set to NULL. If
' pbData is non-NULL and cbData isn't large enough, FALSE is returned
' with a last error of ERRROR_MORE_DATA.
'
' CERT_STORE_SAVE_TO_FILENAME_A:
' CERT_STORE_SAVE_TO_FILENAME_W:
' CERT_STORE_SAVE_TO_FILENAME:
' Opens the file and saves to it. The filename is passed in pvSaveToPara.
' The filename is UNICODE for the "_W" option and ASCII for the "_A"
' option. For "_W": given,
' For "_A": given,
'
'
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate Store close flags
'--------------------------------------------------------------------------
Public Const CERT_CLOSE_STORE_FORCE_FLAG As Long = &H00000001
Public Const CERT_CLOSE_STORE_CHECK_FLAG As Long = &H00000002
'+-------------------------------------------------------------------------
' Close a cert store handle.
'
' There needs to be a corresponding close for each open and duplicate.
'
' Even on the final close, the cert store isn't freed until all of its
' certificate and CRL contexts have also been freed.
'
' On the final close, the hCryptProv passed to CertStoreOpen is
' CryptReleaseContext'ed.
'
' To force the closure of the store with all of its memory freed, set the
' CERT_STORE_CLOSE_FORCE_FLAG. This flag should be set when the caller does
' its own reference counting and wants everything to vanish.
'
' To check if all the store's certificates and CRLs have been freed and that
' this is the last CertCloseStore, set the CERT_CLOSE_STORE_CHECK_FLAG. If
' set and certs, CRLs or stores still need to be freed/closed, FALSE is
' returned with LastError set to CRYPT_E_PENDING_CLOSE. Note, for FALSE,
' the store is still closed. This is a diagnostic flag.
'
' LastError is preserved unless CERT_CLOSE_STORE_CHECK_FLAG is set and FALSE
' is returned.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Get the subject certificate context uniquely identified by its Issuer and
' SerialNumber from the store.
'
' If the certificate isn't found, NULL is returned. Otherwise, a pointer to
' a read only CERT_CONTEXT is returned. CERT_CONTEXT must be freed by calling
' CertFreeCertificateContext. CertDuplicateCertificateContext can be called to make a
' duplicate.
'
' The returned certificate might not be valid. Normally, it would be
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Enumerate the certificate contexts in the store.
'
' If a certificate isn't found, NULL is returned.
' Otherwise, a pointer to a read only CERT_CONTEXT is returned. CERT_CONTEXT
' must be freed by calling CertFreeCertificateContext or is freed when passed as the
' pPrevCertContext on a subsequent call. CertDuplicateCertificateContext
' can be called to make a duplicate.
'
' pPrevCertContext MUST BE NULL to enumerate the first
' certificate in the store. Successive certificates are enumerated by setting
' pPrevCertContext to the CERT_CONTEXT returned by a previous call.
'
' NOTE: a NON-NULL pPrevCertContext is always CertFreeCertificateContext'ed by
' this function, even for an error.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Find the first or next certificate context in the store.
'
' The certificate is found according to the dwFindType and its pvFindPara.
' See below for a list of the find types and its parameters.
'
' Currently dwFindFlags is only used for CERT_FIND_SUBJECT_ATTR,
' CERT_FIND_ISSUER_ATTR or CERT_FIND_CTL_USAGE. Otherwise, must be set to 0.
'
' Usage of dwCertEncodingType depends on the dwFindType.
'
' If the first or next certificate isn't found, NULL is returned.
' Otherwise, a pointer to a read only CERT_CONTEXT is returned. CERT_CONTEXT
' must be freed by calling CertFreeCertificateContext or is freed when passed as the
' pPrevCertContext on a subsequent call. CertDuplicateCertificateContext
' can be called to make a duplicate.
'
' pPrevCertContext MUST BE NULL on the first
' call to find the certificate. To find the next certificate, the
' pPrevCertContext is set to the CERT_CONTEXT returned by a previous call.
'
' NOTE: a NON-NULL pPrevCertContext is always CertFreeCertificateContext'ed by
' this function, even for an error.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate comparison functions
'--------------------------------------------------------------------------
Public Const CERT_COMPARE_MASK As Long = &HFFFF
Public Const CERT_COMPARE_SHIFT As Long = 16
Public Const CERT_COMPARE_ANY As Long = 0
Public Const CERT_COMPARE_SHA1_HASH As Long = 1
Public Const CERT_COMPARE_NAME As Long = 2
Public Const CERT_COMPARE_ATTR As Long = 3
Public Const CERT_COMPARE_MD5_HASH As Long = 4
Public Const CERT_COMPARE_PROPERTY As Long = 5
Public Const CERT_COMPARE_PUBLIC_KEY As Long = 6
Public Const CERT_COMPARE_NAME_STR_A As Long = 7
Public Const CERT_COMPARE_NAME_STR_W As Long = 8
Public Const CERT_COMPARE_KEY_SPEC As Long = 9
Public Const CERT_COMPARE_ENHKEY_USAGE As Long = 10
'+-------------------------------------------------------------------------
' dwFindType
'
' The dwFindType definition consists of two components:
' - comparison function
' - certificate information flag
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CERT_FIND_ANY
'
' Find any certificate.
'
' pvFindPara isn't used.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CERT_FIND_HASH
'
' Find a certificate with the specified hash.
'
' pvFindPara points to a CRYPT_HASH_BLOB.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CERT_FIND_PROPERTY
'
' Find a certificate having the specified property.
'
' pvFindPara points to a DWORD containing the PROP_ID
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CERT_FIND_PUBLIC_KEY
'
' Find a certificate matching the specified public key.
'
' pvFindPara points to a CERT_PUBLIC_KEY_INFO containing the public key
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CERT_FIND_SUBJECT_NAME
' CERT_FIND_ISSUER_NAME
'
' Find a certificate with the specified subject/issuer name. Does an exact
' match of the entire name.
'
' Restricts search to certificates matching the dwCertEncodingType.
'
' pvFindPara points to a CERT_NAME_BLOB.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CERT_FIND_SUBJECT_ATTR
' CERT_FIND_ISSUER_ATTR
'
' Find a certificate with the specified subject/issuer attributes.
'
' Compares the attributes in the subject/issuer name with the
' pvFindPara. The comparison iterates through the CERT_RDN attributes and looks
' for an attribute match in any of the subject/issuer's RDNs.
'
' The CERT_RDN_ATTR fields can have the following special values:
' pszObjId == NULL - ignore the attribute object identifier
' dwValueType == RDN_ANY_TYPE - ignore the value type
' Value.pbData == NULL - match any value
'
' Currently only an exact, case sensitive match is supported.
'
' CERT_UNICODE_IS_RDN_ATTRS_FLAG should be set in dwFindFlags if the RDN was
' initialized with unicode strings as for
'
' Restricts search to certificates matching the dwCertEncodingType.
'
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CERT_FIND_SUBJECT_STR_A
' CERT_FIND_SUBJECT_STR_W | CERT_FIND_SUBJECT_STR
' CERT_FIND_ISSUER_STR_A
' CERT_FIND_ISSUER_STR_W | CERT_FIND_ISSUER_STR
'
' Find a certificate containing the specified subject/issuer name string.
'
' First, the certificate's subject/issuer is converted to a name string
' case insensitive substring within string match is performed.
'
' Restricts search to certificates matching the dwCertEncodingType.
'
' For *_STR_A, pvFindPara points to a null terminated character string.
' For *_STR_W, pvFindPara points to a null terminated wide character string.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CERT_FIND_KEY_SPEC
'
' Find a certificate having a CERT_KEY_SPEC_PROP_ID property matching
' the specified KeySpec.
'
' pvFindPara points to a DWORD containing the KeySpec.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CERT_FIND_ENHKEY_USAGE
'
' Find a certificate having the szOID_ENHANCED_KEY_USAGE extension or
' the CERT_ENHKEY_USAGE_PROP_ID and matching the specified pszUsageIdentifers.
'
' pvFindPara points to a CERT_ENHKEY_USAGE data structure. If pvFindPara
' is NULL or CERT_ENHKEY_USAGE's cUsageIdentifier is 0, then, matches any
' certificate having enhanced key usage.
'
' The CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG can be set in dwFindFlags to
' also match a certificate without either the extension or property.
'
' If CERT_FIND_NO_ENHKEY_USAGE_FLAG is set in dwFindFlags, finds
' certificates without the key usage extension or property. Setting this
' flag takes precedence over pvFindPara being NULL.
'
' If the CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG is set, then, only does a match
' using the extension. If pvFindPara is NULL or cUsageIdentifier is set to
' 0, finds certificates having the extension. If
' CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG is set, also matches a certificate
' without the extension. If CERT_FIND_NO_ENHKEY_USAGE_FLAG is set, finds
' certificates without the extension.
'
' If the CERT_FIND_EXT_PROP_ENHKEY_USAGE_FLAG is set, then, only does a match
' using the property. If pvFindPara is NULL or cUsageIdentifier is set to
' 0, finds certificates having the property. If
' CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG is set, also matches a certificate
' without the property. If CERT_FIND_NO_ENHKEY_USAGE_FLAG is set, finds
' certificates without the property.
'--------------------------------------------------------------------------
Public Const CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG As Long = &H1
Public Const CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG As Long = &H2
Public Const CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG As Long = &H4
Public Const CERT_FIND_NO_ENHKEY_USAGE_FLAG As Long = &H8
'+-------------------------------------------------------------------------
' Get the certificate context from the store for the first or next issuer
' of the specified subject certificate. Perform the enabled
' using the returned issuer certificate.)
'
' If the first or next issuer certificate isn't found, NULL is returned.
' Otherwise, a pointer to a read only CERT_CONTEXT is returned. CERT_CONTEXT
' must be freed by calling CertFreeCertificateContext or is freed when passed as the
' pPrevIssuerContext on a subsequent call. CertDuplicateCertificateContext
' can be called to make a duplicate.
'
' For a self signed subject certificate, NULL is returned with LastError set
' to CERT_STORE_SELF_SIGNED. The enabled verification checks are still done.
'
' The pSubjectContext may have been obtained from this store, another store
' or created by the caller application. When created by the caller, the
' CertCreateCertificateContext function must have been called.
'
' An issuer may have multiple certificates. This may occur when the validity
' period is about to change. pPrevIssuerContext MUST BE NULL on the first
' call to get the issuer. To get the next certificate for the issuer, the
' pPrevIssuerContext is set to the CERT_CONTEXT returned by a previous call.
'
' NOTE: a NON-NULL pPrevIssuerContext is always CertFreeCertificateContext'ed by
' this function, even for an error.
'
' The following flags can be set in *pdwFlags to enable verification checks
' on the subject certificate context:
' CERT_STORE_SIGNATURE_FLAG - use the public key in the returned
' issuer certificate to verify the
' signature on the subject certificate.
' Note, if pSubjectContext->hCertStore ==
' hCertStore, the store provider might
' be able to eliminate a redo of
' the signature verify.
' CERT_STORE_TIME_VALIDITY_FLAG - get the current time and verify that
' its within the subject certificate's
' validity period
' CERT_STORE_REVOCATION_FLAG - check if the subject certificate is on
' the issuer's revocation list
'
' If an enabled verification check fails, then, its flag is set upon return.
' If CERT_STORE_REVOCATION_FLAG was enabled and the issuer doesn't have a
' CRL in the store, then, CERT_STORE_NO_CRL_FLAG is set in addition to
' the CERT_STORE_REVOCATION_FLAG.
'
' If CERT_STORE_SIGNATURE_FLAG or CERT_STORE_REVOCATION_FLAG is set, then,
' CERT_STORE_NO_ISSUER_FLAG is set if it doesn't have an issuer certificate
' in the store.
'
' For a verification check failure, a pointer to the issuer's CERT_CONTEXT
' is still returned and SetLastError isn't updated.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Perform the enabled verification checks on the subject certificate
' using the issuer. Same checks and flags definitions as for the above
' CertGetIssuerCertificateFromStore.
'
' If you are only checking CERT_STORE_TIME_VALIDITY_FLAG, then, the
' issuer can be NULL.
'
' For a verification check failure, SUCCESS is still returned.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Duplicate a certificate context
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Create a certificate context from the encoded certificate. The created
' context isn't put in a store.
'
' Makes a copy of the encoded certificate in the created context.
'
' If unable to decode and create the certificate context, NULL is returned.
' Otherwise, a pointer to a read only CERT_CONTEXT is returned.
' CERT_CONTEXT must be freed by calling CertFreeCertificateContext.
' CertDuplicateCertificateContext can be called to make a duplicate.
'
' CertSetCertificateContextProperty and CertGetCertificateContextProperty can be called
' to store properties for the certificate.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Free a certificate context
'
' There needs to be a corresponding free for each context obtained by a
' get, find, duplicate or create.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Set the property for the specified certificate context.
'
' The type definition for pvData depends on the dwPropId value. There are
' five predefined types:
' CERT_KEY_PROV_HANDLE_PROP_ID - a HCRYPTPROV for the certificate's
' private key is passed in pvData. Updates the hCryptProv field
' of the CERT_KEY_CONTEXT_PROP_ID. If the CERT_KEY_CONTEXT_PROP_ID
' doesn't exist, its created with all the other fields zeroed out. If
' CERT_STORE_NO_CRYPT_RELEASE_FLAG isn't set, HCRYPTPROV is implicitly
' released when either the property is set to NULL or on the final
' free of the CertContext.
'
' CERT_KEY_PROV_INFO_PROP_ID - a PCRYPT_KEY_PROV_INFO for the certificate's
' private key is passed in pvData.
'
' CERT_SHA1_HASH_PROP_ID -
' CERT_MD5_HASH_PROP_ID - normally, either property is implicitly
' set by doing a CertGetCertificateContextProperty. pvData points to a
' CRYPT_HASH_BLOB.
'
' CERT_KEY_CONTEXT_PROP_ID - a PCERT_KEY_CONTEXT for the certificate's
' private key is passed in pvData. The CERT_KEY_CONTEXT contains both the
' hCryptProv and dwKeySpec for the private key.
' See the CERT_KEY_PROV_HANDLE_PROP_ID for more information about
' the hCryptProv field and dwFlags settings. Note, more fields may
' be added for this property. The cbSize field value will be adjusted
' accordingly.
'
' CERT_KEY_SPEC_PROP_ID - the dwKeySpec for the private key. pvData
' points to a DWORD containing the KeySpec
'
' CERT_ENHKEY_USAGE_PROP_ID - enhanced key usage definition for the
' certificate. pvData points to a CRYPT_DATA_BLOB containing an
'
' CERT_NEXT_UPDATE_LOCATION_PROP_ID - location of the next update.
' Currently only applicable to CTLs. pvData points to a CRYPT_DATA_BLOB
'
' CERT_FRIENDLY_NAME_PROP_ID - friendly name for the cert, CRL or CTL.
' pvData points to a CRYPT_DATA_BLOB. pbData is a pointer to a NULL
' terminated unicode, wide character string.
'
' For all the other PROP_IDs: an encoded PCRYPT_DATA_BLOB is passed in pvData.
'
' If the property already exists, then, the old value is deleted and silently
' replaced. Setting, pvData to NULL, deletes the property.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Get the property for the specified certificate context.
'
' For CERT_KEY_PROV_HANDLE_PROP_ID, pvData points to a HCRYPTPROV.
'
' For CERT_KEY_PROV_INFO_PROP_ID, pvData points to a CRYPT_KEY_PROV_INFO structure.
' Elements pointed to by fields in the pvData structure follow the
' structure. Therefore, *pcbData may exceed the size of the structure.
'
' For CERT_KEY_CONTEXT_PROP_ID, pvData points to a CERT_KEY_CONTEXT structure.
'
' For CERT_KEY_SPEC_PROP_ID, pvData points to a DWORD containing the KeySpec.
' If the CERT_KEY_CONTEXT_PROP_ID exists, the KeySpec is obtained from there.
' Otherwise, if the CERT_KEY_PROV_INFO_PROP_ID exists, its the source
' of the KeySpec.
'
' For CERT_SHA1_HASH_PROP_ID or CERT_MD5_HASH_PROP_ID, if the hash
' and then set. pvData points to the computed hash. Normally, the length
' is 20 bytes for SHA and 16 for MD5.
'
' For all other PROP_IDs, pvData points to an encoded array of bytes.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Enumerate the properties for the specified certificate context.
'
' To get the first property, set dwPropId to 0. The ID of the first
' property is returned. To get the next property, set dwPropId to the
' ID returned by the last call. To enumerate all the properties continue
' until 0 is returned.
'
' CertGetCertificateContextProperty is called to get the property's data.
'
' Note, since, the CERT_KEY_PROV_HANDLE_PROP_ID and CERT_KEY_SPEC_PROP_ID
' properties are stored as fields in the CERT_KEY_CONTEXT_PROP_ID
' property, they aren't enumerated individually.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Get the first or next CRL context from the store for the specified
' issuer certificate. Perform the enabled verification checks on the CRL.
'
' If the first or next CRL isn't found, NULL is returned.
' Otherwise, a pointer to a read only CRL_CONTEXT is returned. CRL_CONTEXT
' must be freed by calling CertFreeCRLContext. However, the free must be
' pPrevCrlContext on a subsequent call. CertDuplicateCRLContext
' can be called to make a duplicate.
'
' The pIssuerContext may have been obtained from this store, another store
' or created by the caller application. When created by the caller, the
' CertCreateCertificateContext function must have been called.
'
' If pIssuerContext == NULL, finds all the CRLs in the store.
'
' An issuer may have multiple CRLs. For example, it generates delta CRLs
' using a X.509 v3 extension. pPrevCrlContext MUST BE NULL on the first
' call to get the CRL. To get the next CRL for the issuer, the
' pPrevCrlContext is set to the CRL_CONTEXT returned by a previous call.
'
' NOTE: a NON-NULL pPrevCrlContext is always CertFreeCRLContext'ed by
' this function, even for an error.
'
' The following flags can be set in *pdwFlags to enable verification checks
' on the returned CRL:
' CERT_STORE_SIGNATURE_FLAG - use the public key in the
' issuer's certificate to verify the
' signature on the returned CRL.
' Note, if pIssuerContext->hCertStore ==
' hCertStore, the store provider might
' be able to eliminate a redo of
' the signature verify.
' CERT_STORE_TIME_VALIDITY_FLAG - get the current time and verify that
' its within the CRL's ThisUpdate and
' NextUpdate validity period.
'
' If an enabled verification check fails, then, its flag is set upon return.
'
' If pIssuerContext == NULL, then, an enabled CERT_STORE_SIGNATURE_FLAG
' always fails and the CERT_STORE_NO_ISSUER_FLAG is also set.
'
' For a verification check failure, a pointer to the first or next
' CRL_CONTEXT is still returned and SetLastError isn't updated.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Duplicate a CRL context
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Create a CRL context from the encoded CRL. The created
' context isn't put in a store.
'
' Makes a copy of the encoded CRL in the created context.
'
' If unable to decode and create the CRL context, NULL is returned.
' Otherwise, a pointer to a read only CRL_CONTEXT is returned.
' CRL_CONTEXT must be freed by calling CertFreeCRLContext.
' CertDuplicateCRLContext can be called to make a duplicate.
'
' CertSetCRLContextProperty and CertGetCRLContextProperty can be called
' to store properties for the CRL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Free a CRL context
'
' There needs to be a corresponding free for each context obtained by a
' get, duplicate or create.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Set the property for the specified CRL context.
'
' Same Property Ids and semantics as CertSetCertificateContextProperty.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Get the property for the specified CRL context.
'
' Same Property Ids and semantics as CertGetCertificateContextProperty.
'
' CERT_SHA1_HASH_PROP_ID or CERT_MD5_HASH_PROP_ID is the predefined
' property of most interest.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Enumerate the properties for the specified CRL context.
'
' To get the first property, set dwPropId to 0. The ID of the first
' property is returned. To get the next property, set dwPropId to the
' ID returned by the last call. To enumerate all the properties continue
' until 0 is returned.
'
' CertGetCRLContextProperty is called to get the property's data.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Add certificate/CRL, encoded, context or element disposition values.
'--------------------------------------------------------------------------
Public Const CERT_STORE_ADD_NEW As Long = 1
Public Const CERT_STORE_ADD_USE_EXISTING As Long = 2
Public Const CERT_STORE_ADD_REPLACE_EXISTING As Long = 3
Public Const CERT_STORE_ADD_ALWAYS As Long = 4
'+-------------------------------------------------------------------------
' Add the encoded certificate to the store according to the specified
' disposition action.
'
' Makes a copy of the encoded certificate before adding to the store.
'
' dwAddDispostion specifies the action to take if the certificate
' already exists in the store. This parameter must be one of the following
' values:
' CERT_STORE_ADD_NEW
' Fails if the certificate already exists in the store. LastError
' is set to CRYPT_E_EXISTS.
' CERT_STORE_ADD_USE_EXISTING
' If the certifcate already exists, then, its used and if ppCertContext
' is non-NULL, the existing context is duplicated.
' CERT_STORE_ADD_REPLACE_EXISTING
' If the certificate already exists, then, the existing certificate
' context is deleted before creating and adding the new context.
' CERT_STORE_ADD_ALWAYS
' No check is made to see if the certificate already exists. A
' new certificate context is always created. This may lead to
' duplicates in the store.
'
' CertGetSubjectCertificateFromStore is called to determine if the
' certificate already exists in the store.
'
' ppCertContext can be NULL, indicating the caller isn't interested
' in getting the CERT_CONTEXT of the added or existing certificate.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Add the certificate context to the store according to the specified
' disposition action.
'
' In addition to the encoded certificate, the context's properties are
' CERT_KEY_PROV_HANDLE_PROP_ID or CERT_KEY_SPEC_PROP_ID) isn't copied.
'
' Makes a copy of the certificate context before adding to the store.
'
' dwAddDispostion specifies the action to take if the certificate
' already exists in the store. This parameter must be one of the following
' values:
' CERT_STORE_ADD_NEW
' Fails if the certificate already exists in the store. LastError
' is set to CRYPT_E_EXISTS.
' CERT_STORE_ADD_USE_EXISTING
' If the certifcate already exists, then, its used and if ppStoreContext
' is non-NULL, the existing context is duplicated. Iterates
' through pCertContext's properties and only copies the properties
' that don't already exist. The SHA1 and MD5 hash properties aren't
' copied.
' CERT_STORE_ADD_REPLACE_EXISTING
' If the certificate already exists, then, the existing certificate
' context is deleted before creating and adding a new context.
' Properties are copied before doing the add.
' CERT_STORE_ADD_ALWAYS
' No check is made to see if the certificate already exists. A
' new certificate context is always created and added. This may lead to
' duplicates in the store. Properties are
' copied before doing the add.
'
' CertGetSubjectCertificateFromStore is called to determine if the
' certificate already exists in the store.
'
' ppStoreContext can be NULL, indicating the caller isn't interested
' in getting the CERT_CONTEXT of the added or existing certificate.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate Store Context Types
'--------------------------------------------------------------------------
Public Const CERT_STORE_CERTIFICATE_CONTEXT As Long = 1
Public Const CERT_STORE_CRL_CONTEXT As Long = 2
Public Const CERT_STORE_CTL_CONTEXT As Long = 3
'+-------------------------------------------------------------------------
' Certificate Store Context Bit Flags
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Add the serialized certificate or CRL element to the store.
'
' The serialized element contains the encoded certificate, CRL or CTL and
' its properties, such as, CERT_KEY_PROV_INFO_PROP_ID.
'
' If hCertStore is NULL, creates a certificate, CRL or CTL context not
' residing in any store.
'
' dwAddDispostion specifies the action to take if the certificate or CRL
' already exists in the store. See CertAddCertificateContextToStore for a

' list of and actions taken.
'
' dwFlags currently isn't used and should be set to 0.
'
' dwContextTypeFlags specifies the set of allowable contexts. For example, to
' add either a certificate or CRL, set dwContextTypeFlags to:
' CERT_STORE_CERTIFICATE_CONTEXT_FLAG | CERT_STORE_CRL_CONTEXT_FLAG
'
' *pdwContextType is updated with the type of the context returned in
' *ppvContxt. pdwContextType or ppvContext can be NULL, indicating the
' caller isn't interested in getting the output. If *ppvContext is
' returned it must be freed by calling CertFreeCertificateContext or
' CertFreeCRLContext.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Delete the specified certificate from the store.
'
' All subsequent gets or finds for the certificate will fail. However,
' memory allocated for the certificate isn't freed until all of its contexts
' have also been freed.
'
' The pCertContext is obtained from a get, enum, find or duplicate.
'
' Some store provider implementations might also delete the issuer's CRLs
' if this is the last certificate for the issuer in the store.
'
' NOTE: the pCertContext is always CertFreeCertificateContext'ed by
' this function, even for an error.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Add the encoded CRL to the store according to the specified
' disposition option.
'
' Makes a copy of the encoded CRL before adding to the store.
'
' dwAddDispostion specifies the action to take if the CRL
' already exists in the store. See CertAddEncodedCertificateToStore for a
' list of and actions taken.
'
' Compares the CRL's Issuer to determine if the CRL already exists in the
' store.
'
' ppCrlContext can be NULL, indicating the caller isn't interested
' in getting the CRL_CONTEXT of the added or existing CRL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Add the CRL context to the store according to the specified
' disposition option.
'
' In addition to the encoded CRL, the context's properties are
' CERT_KEY_PROV_HANDLE_PROP_ID or CERT_KEY_SPEC_PROP_ID) isn't copied.
'
' Makes a copy of the encoded CRL before adding to the store.
'
' dwAddDispostion specifies the action to take if the CRL
' already exists in the store. See CertAddCertificateContextToStore for a
' list of and actions taken.
'
' Compares the CRL's Issuer, ThisUpdate and NextUpdate to determine
' if the CRL already exists in the store.
'
' ppStoreContext can be NULL, indicating the caller isn't interested
' in getting the CRL_CONTEXT of the added or existing CRL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Delete the specified CRL from the store.
'
' All subsequent gets for the CRL will fail. However,
' memory allocated for the CRL isn't freed until all of its contexts
' have also been freed.
'
' The pCrlContext is obtained from a get or duplicate.
'
' NOTE: the pCrlContext is always CertFreeCRLContext'ed by
' this function, even for an error.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Serialize the certificate context's encoded certificate and its
' properties.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Serialize the CRL context's encoded CRL and its properties.
'--------------------------------------------------------------------------
'+=========================================================================
'==========================================================================
'+-------------------------------------------------------------------------
' Duplicate a CTL context
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Create a CTL context from the encoded CTL. The created
' context isn't put in a store.
'
' Makes a copy of the encoded CTL in the created context.
'
' If unable to decode and create the CTL context, NULL is returned.
' Otherwise, a pointer to a read only CTL_CONTEXT is returned.
' CTL_CONTEXT must be freed by calling CertFreeCTLContext.
' CertDuplicateCTLContext can be called to make a duplicate.
'
' CertSetCTLContextProperty and CertGetCTLContextProperty can be called
' to store properties for the CTL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Free a CTL context
'
' There needs to be a corresponding free for each context obtained by a
' get, duplicate or create.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Set the property for the specified CTL context.
'
' Same Property Ids and semantics as CertSetCertificateContextProperty.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Get the property for the specified CTL context.
'
' Same Property Ids and semantics as CertGetCertificateContextProperty.
'
' CERT_SHA1_HASH_PROP_ID or CERT_NEXT_UPDATE_LOCATION_PROP_ID are the
' predefined properties of most interest.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Enumerate the properties for the specified CTL context.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Enumerate the CTL contexts in the store.
'
' If a CTL isn't found, NULL is returned.
' Otherwise, a pointer to a read only CTL_CONTEXT is returned. CTL_CONTEXT
' must be freed by calling CertFreeCTLContext or is freed when passed as the
' pPrevCtlContext on a subsequent call. CertDuplicateCTLContext
' can be called to make a duplicate.
'
' pPrevCtlContext MUST BE NULL to enumerate the first
' CTL in the store. Successive CTLs are enumerated by setting
' pPrevCtlContext to the CTL_CONTEXT returned by a previous call.
'
' NOTE: a NON-NULL pPrevCtlContext is always CertFreeCTLContext'ed by
' this function, even for an error.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Attempt to find the specified subject in the CTL.
'
' For CTL_CERT_SUBJECT_TYPE, pvSubject points to a CERT_CONTEXT. The CTL's
' SubjectAlgorithm is examined to determine the representation of the
' subject's identity. Initially, only SHA1 or MD5 hash will be supported.
' The appropriate hash property is obtained from the CERT_CONTEXT.
'
' For CTL_ANY_SUBJECT_TYPE, pvSubject points to the CTL_ANY_SUBJECT_INFO
' structure which contains the SubjectAlgorithm to be matched in the CTL
' and the SubjectIdentifer to be matched in one of the CTL entries.
'
' The certificate's hash or the CTL_ANY_SUBJECT_INFO's SubjectIdentifier
' is used as the key in searching the subject entries. A binary
' memory comparison is done between the key and the entry's SubjectIdentifer.
'
' dwEncodingType isn't used for either of the above SubjectTypes.
'--------------------------------------------------------------------------
' Subject Types:
' CTL_ANY_SUBJECT_TYPE, pvSubject points to following CTL_ANY_SUBJECT_INFO.
' CTL_CERT_SUBJECT_TYPE, pvSubject points to CERT_CONTEXT.
Public Const CTL_ANY_SUBJECT_TYPE As Long = 1
Public Const CTL_CERT_SUBJECT_TYPE As Long = 2
'+-------------------------------------------------------------------------
' Find the first or next CTL context in the store.
'
' The CTL is found according to the dwFindType and its pvFindPara.
' See below for a list of the find types and its parameters.
'
' Currently dwFindFlags isn't used and must be set to 0.
'
' Usage of dwMsgAndCertEncodingType depends on the dwFindType.
'
' If the first or next CTL isn't found, NULL is returned.
' Otherwise, a pointer to a read only CTL_CONTEXT is returned. CTL_CONTEXT
' must be freed by calling CertFreeCTLContext or is freed when passed as the
' pPrevCtlContext on a subsequent call. CertDuplicateCTLContext
' can be called to make a duplicate.
'
' pPrevCtlContext MUST BE NULL on the first
' call to find the CTL. To find the next CTL, the
' pPrevCtlContext is set to the CTL_CONTEXT returned by a previous call.
'
' NOTE: a NON-NULL pPrevCtlContext is always CertFreeCTLContext'ed by
' this function, even for an error.
'--------------------------------------------------------------------------
Public Const CTL_FIND_ANY As Long = 0
Public Const CTL_FIND_SHA1_HASH As Long = 1
Public Const CTL_FIND_MD5_HASH As Long = 2
Public Const CTL_FIND_USAGE As Long = 3
Public Const CTL_FIND_SUBJECT As Long = 4
Public Const CTL_FIND_NO_LIST_ID_CBDATA As Long = &HFFFFFFFF
Public Const CTL_FIND_SAME_USAGE_FLAG As Long = &H1
'+-------------------------------------------------------------------------
' CTL_FIND_ANY
'
' Find any CTL.
'
' pvFindPara isn't used.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CTL_FIND_SHA1_HASH
' CTL_FIND_MD5_HASH
'
' Find a CTL with the specified hash.
'
' pvFindPara points to a CRYPT_HASH_BLOB.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CTL_FIND_USAGE
'
' Find a CTL having the specified usage identifiers, list identifier or
' signer. The CertEncodingType of the signer is obtained from the
' dwMsgAndCertEncodingType parameter.
'
' pvFindPara points to a CTL_FIND_USAGE_PARA data structure. The
' SubjectUsage.cUsageIdentifer can be 0 to match any usage. The
' ListIdentifier.cbData can be 0 to match any list identifier. To only match
' CTLs without a ListIdentifier, cbData must be set to
' CTL_FIND_NO_LIST_ID_CBDATA. pSigner can be NULL to match any signer. Only
' the Issuer and SerialNumber fields of the pSigner's PCERT_INFO are used.
' To only match CTLs without a signer, pSigner must be set to
' CTL_FIND_NO_SIGNER_PTR.
'
' The CTL_FIND_SAME_USAGE_FLAG can be set in dwFindFlags to
' only match CTLs with the same usage identifiers. CTLs having additional
' usage identifiers aren't matched. For example, if only "1.2.3" is specified
' in CTL_FIND_USAGE_PARA, then, for a match, the CTL must only contain
' "1.2.3" and not any additional usage identifers.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' CTL_FIND_SUBJECT
'
' Find a CTL having the specified subject. CertFindSubjectInCTL can be
' called to get a pointer to the subject's entry in the CTL. pUsagePara can
' optionally be set to enable the above CTL_FIND_USAGE matching.
'
' pvFindPara points to a CTL_FIND_SUBJECT_PARA data structure.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Add the encoded CTL to the store according to the specified
' disposition option.
'
' Makes a copy of the encoded CTL before adding to the store.
'
' dwAddDispostion specifies the action to take if the CTL
' already exists in the store. See CertAddEncodedCertificateToStore for a
' list of and actions taken.
'
' Compares the CTL's SubjectUsage, ListIdentifier and any of its signers
' to determine if the CTL already exists in the store.
'
' ppCtlContext can be NULL, indicating the caller isn't interested
' in getting the CTL_CONTEXT of the added or existing CTL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Add the CTL context to the store according to the specified
' disposition option.
'
' In addition to the encoded CTL, the context's properties are
' CERT_KEY_PROV_HANDLE_PROP_ID or CERT_KEY_SPEC_PROP_ID) isn't copied.
'
' Makes a copy of the encoded CTL before adding to the store.
'
' dwAddDispostion specifies the action to take if the CTL
' already exists in the store. See CertAddCertificateContextToStore for a
' list of and actions taken.
'
' Compares the CTL's SubjectUsage, ListIdentifier and any of its signers
' to determine if the CTL already exists in the store.
'
' ppStoreContext can be NULL, indicating the caller isn't interested
' in getting the CTL_CONTEXT of the added or existing CTL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Serialize the CTL context's encoded CTL and its properties.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Delete the specified CTL from the store.
'
' All subsequent gets for the CTL will fail. However,
' memory allocated for the CTL isn't freed until all of its contexts
' have also been freed.
'
' The pCtlContext is obtained from a get or duplicate.
'
' NOTE: the pCtlContext is always CertFreeCTLContext'ed by
' this function, even for an error.
'--------------------------------------------------------------------------
'+=========================================================================
' Enhanced Key Usage Helper Functions
'==========================================================================
'+-------------------------------------------------------------------------
' Get the enhanced key usage extension or property from the certificate
' and decode.
'
' If the CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG is set, then, only get the
' extension.
'
' If the CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG is set, then, only get the
' property.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Set the enhanced key usage property for the certificate.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Add the usage identifier to the certificate's enhanced key usage property.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Remove the usage identifier from the certificate's enhanced key usage
' property.
'--------------------------------------------------------------------------
'+=========================================================================
' Cryptographic Message helper functions for verifying and signing a
' CTL.
'==========================================================================
'+-------------------------------------------------------------------------
' Get and verify the signer of a cryptographic message.
'
' To verify a CTL, the hCryptMsg is obtained from the CTL_CONTEXT's
' hCryptMsg field.
'
' If CMSG_TRUSTED_SIGNER_FLAG is set, then, treat the Signer stores as being
' trusted and only search them to find the certificate corresponding to the
' signer's issuer and serial number. Otherwise, the SignerStores are
' optionally provided to supplement the message's store of certificates.
' If a signer certificate is found, its public key is used to verify
' the message signature. The CMSG_SIGNER_ONLY_FLAG can be set to
' return the signer without doing the signature verify.
'
' If CMSG_USE_SIGNER_INDEX_FLAG is set, then, only get the signer specified
' by *pdwSignerIndex. Otherwise, iterate through all the signers
' until a signer verifies or no more signers.
'
' For a verified signature, *ppSigner is updated with certificate context
' of the signer and *pdwSignerIndex is updated with the index of the signer.
' ppSigner and/or pdwSignerIndex can be NULL, indicating the caller isn't
' interested in getting the CertContext and/or index of the signer.
'--------------------------------------------------------------------------
Public Const CMSG_TRUSTED_SIGNER_FLAG As Long = &H1
Public Const CMSG_SIGNER_ONLY_FLAG As Long = &H2
Public Const CMSG_USE_SIGNER_INDEX_FLAG As Long = &H4
'+-------------------------------------------------------------------------
' Sign an encoded CTL.
'
' The pbCtlContent can be obtained via a CTL_CONTEXT's pbCtlContent
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Encode the CTL and create a signed message containing the encoded CTL.
'--------------------------------------------------------------------------
'+=========================================================================
' Certificate Verify CTL Usage Data Structures and APIs
'==========================================================================
Public Const CERT_VERIFY_INHIBIT_CTL_UPDATE_FLAG As Long = &H1
Public Const CERT_VERIFY_TRUSTED_SIGNERS_FLAG As Long = &H2
Public Const CERT_VERIFY_NO_TIME_CHECK_FLAG As Long = &H4
Public Const CERT_VERIFY_ALLOW_MORE_USAGE_FLAG As Long = &H8
Public Const CERT_VERIFY_UPDATED_CTL_FLAG As Long = &H1
'+-------------------------------------------------------------------------
' Verify that a subject is trusted for the specified usage by finding a
' signed and time valid CTL with the usage identifiers and containing the
' the subject. A subject can be identified by either its certificate context
' or any identifier such as its SHA1 hash.
'
' See CertFindSubjectInCTL for definition of dwSubjectType and pvSubject
' parameters.
'
' Via pVerifyUsagePara, the caller can specify the stores to be searched
' to find the CTL. The caller can also specify the stores containing
' acceptable CTL signers. By setting the ListIdentifier, the caller
' can also restrict to a particular signer CTL list.
'
' Via pVerifyUsageStatus, the CTL containing the subject, the subject's
' index into the CTL's array of entries, and the signer of the CTL
' are returned. If the caller is not interested, ppCtl and ppSigner can be set
' to NULL. Returned contexts must be freed via the store's free context APIs.
'
' If the CERT_VERIFY_INHIBIT_CTL_UPDATE_FLAG isn't set, then, a time
' invalid CTL in one of the CtlStores may be replaced. When replaced, the
' CERT_VERIFY_UPDATED_CTL_FLAG is set in pVerifyUsageStatus->dwFlags.
'
' If the CERT_VERIFY_TRUSTED_SIGNERS_FLAG is set, then, only the
' SignerStores specified in pVerifyUsageStatus are searched to find
' the signer. Otherwise, the SignerStores provide additional sources
' to find the signer's certificate.
'
' If CERT_VERIFY_NO_TIME_CHECK_FLAG is set, then, the CTLs aren't checked
' for time validity.
'
' If CERT_VERIFY_ALLOW_MORE_USAGE_FLAG is set, then, the CTL may contain
' additional usage identifiers than specified by pSubjectUsage. Otherwise,
' the found CTL will contain the same usage identifers and no more.
'
' CertVerifyCTLUsage will be implemented as a dispatcher to OID installable
' functions. First, it will try to find an OID function matching the first
' usage object identifier in the pUsage sequence. Next, it will dispatch
' to the default CertDllVerifyCTLUsage functions.
'
' If the subject is trusted for the specified usage, then, TRUE is
' returned. Otherwise, FALSE is returned with dwError set to one of the
' following:
' CRYPT_E_NO_VERIFY_USAGE_DLL
' CRYPT_E_NO_VERIFY_USAGE_CHECK
' CRYPT_E_VERIFY_USAGE_OFFLINE
' CRYPT_E_NOT_IN_CTL
' CRYPT_E_NO_TRUSTED_SIGNER
'--------------------------------------------------------------------------
'+=========================================================================
' Certificate Revocation Data Structures and APIs
'==========================================================================
'+-------------------------------------------------------------------------
' The following data structure may be passed to CertVerifyRevocation to
' assist in finding the issuer of the context to be verified.
'
' When pIssuerCert is specified, pIssuerCert is the issuer of
' rgpvContext[cContext - 1].
'
' When cCertStore and rgCertStore are specified, these stores may contain
' an issuer certificate.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' The following data structure is returned by CertVerifyRevocation to
' specify the status of the revoked or unchecked context. Review the
' following CertVerifyRevocation comments for details.
'
' Upon input to CertVerifyRevocation, cbSize must be set to a size
' returns FALSE and sets LastError to E_INVALIDARG.
'
' Upon input to the installed or registered CRYPT_OID_VERIFY_REVOCATION_FUNC
' functions, the dwIndex, dwError and dwReason have been zero'ed.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verifies the array of contexts for revocation. The dwRevType parameter
' indicates the type of the context data structure passed in rgpvContext.
' Currently only the revocation of certificates is defined.
'
' If the CERT_VERIFY_REV_CHAIN_FLAG flag is set, then, CertVerifyRevocation
' is verifying a chain of certs where, rgpvContext[i + 1] is the issuer
' of rgpvContext[i]. Otherwise, CertVerifyRevocation makes no assumptions
' about the order of the contexts.
'
' To assist in finding the issuer, the pRevPara may optionally be set. See
' the CERT_REVOCATION_PARA data structure for details.
'
' The contexts must contain enough information to allow the
' installable or registered revocation DLLs to find the revocation server. For
' certificates, this information would normally be conveyed in an
' extension such as the IETF's AuthorityInfoAccess extension.
'
' CertVerifyRevocation returns TRUE if all of the contexts were successfully
' checked and none were revoked. Otherwise, returns FALSE and updates the
' returned pRevStatus data structure as follows:
' dwIndex
' Index of the first context that was revoked or unable to
' be checked for revocation
' dwError
' Error status. LastError is also set to this error status.
' dwError can be set to one of the following error codes defined
' in winerror.h:
' ERROR_SUCCESS - good context
' CRYPT_E_REVOKED - context was revoked. dwReason contains the
' reason for revocation
' CRYPT_E_REVOCATION_OFFLINE - unable to connect to the
' revocation server
' CRYPT_E_NOT_IN_REVOCATION_DATABASE - the context to be checked
' was not found in the revocation server's database.
' CRYPT_E_NO_REVOCATION_CHECK - the called revocation function
' wasn't able to do a revocation check on the context
' CRYPT_E_NO_REVOCATION_DLL - no installed or registered Dll was
' found to verify revocation
' dwReason
' The dwReason is currently only set for CRYPT_E_REVOKED and contains
' the reason why the context was revoked. May be one of the following
' CRL_REASON_UNSPECIFIED 0
' CRL_REASON_KEY_COMPROMISE 1
' CRL_REASON_CA_COMPROMISE 2
' CRL_REASON_AFFILIATION_CHANGED 3
' CRL_REASON_SUPERSEDED 4
' CRL_REASON_CESSATION_OF_OPERATION 5
' CRL_REASON_CERTIFICATE_HOLD 6
'
' For each entry in rgpvContext, CertVerifyRevocation iterates
' through the CRYPT_OID_VERIFY_REVOCATION_FUNC
' function set's list of installed DEFAULT functions.
' CryptGetDefaultOIDFunctionAddress is called with pwszDll = NULL. If no
' installed functions are found capable of doing the revocation verification,
' CryptVerifyRevocation iterates through CRYPT_OID_VERIFY_REVOCATION_FUNC's
' list of registered DEFAULT Dlls. CryptGetDefaultOIDDllList is called to
' get the list. CryptGetDefaultOIDFunctionAddress is called to load the Dll.
'
' The called functions have the same signature as CertVerifyRevocation. A
' called function returns TRUE if it was able to successfully check all of
' the contexts and none were revoked. Otherwise, the called function returns
' FALSE and updates pRevStatus. dwIndex is set to the index of
' the first context that was found to be revoked or unable to be checked.
' dwError and LastError are updated. For CRYPT_E_REVOKED, dwReason
' is updated. Upon input to the called function, dwIndex, dwError and
' dwReason have been zero'ed. cbSize has been checked to be >=
'
' If the called function returns FALSE, and dwError isn't set to
' CRYPT_E_REVOKED, then, CertVerifyRevocation either continues on to the
' next DLL in the list for a returned dwIndex of 0 or for a returned
' dwIndex > 0, restarts the process of finding a verify function by
' advancing the start of the context array to the returned dwIndex and
' decrementing the count of remaining contexts.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Revocation types
'--------------------------------------------------------------------------
Public Const CERT_CONTEXT_REVOCATION_TYPE As Long = 1
'+-------------------------------------------------------------------------
' When the following flag is set, rgpvContext[] consists of a chain
' of certificates, where rgpvContext[i + 1] is the issuer of rgpvContext[i].
'--------------------------------------------------------------------------
Public Const CERT_VERIFY_REV_CHAIN_FLAG As Long = &H1
'+-------------------------------------------------------------------------
' CERT_CONTEXT_REVOCATION_TYPE
'
' pvContext points to a const CERT_CONTEXT.
'--------------------------------------------------------------------------
'+=========================================================================
' Certificate Helper APIs
'==========================================================================
'+-------------------------------------------------------------------------
' Compare two multiple byte integer blobs to see if they are identical.
'
' Before doing the comparison, leading zero bytes are removed from a
' positive number and leading 0xFF bytes are removed from a negative
' number.
'
' The multiple byte integers are treated as Little Endian. pbData[0] is the
' least significant byte and pbData[cbData - 1] is the most significant
' byte.
'
' Returns TRUE if the integer blobs are identical after removing leading
' 0 or 0xFF bytes.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Compare two certificates to see if they are identical.
'
' Since a certificate is uniquely identified by its Issuer and SerialNumber,
' these are the only fields needing to be compared.
'
' Returns TRUE if the certificates are identical.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Compare two certificate names to see if they are identical.
'
' Returns TRUE if the names are identical.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Compare the attributes in the certificate name with the specified
' The comparison iterates through the CERT_RDN attributes and looks for an
' attribute match in any of the certificate name's RDNs.
' Returns TRUE if all the attributes are found and match.
'
' The CERT_RDN_ATTR fields can have the following special values:
' pszObjId == NULL - ignore the attribute object identifier
' dwValueType == RDN_ANY_TYPE - ignore the value type
'
' Currently only an exact, case sensitive match is supported.
'
' CERT_UNICODE_IS_RDN_ATTRS_FLAG should be set if the pRDN was initialized
'--------------------------------------------------------------------------
Public Const CERT_UNICODE_IS_RDN_ATTRS_FLAG As Long = &H1
'+-------------------------------------------------------------------------
' Compare two public keys to see if they are identical.
'
' Returns TRUE if the keys are identical.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verify the signature of a subject certificate or a CRL using the
' public key info
'
' Returns TRUE for a valid signature.
'
' hCryptProv specifies the crypto provider to use to verify the signature.
' It doesn't need to use a private key.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Compute the hash of the "to be signed" information in the encoded
'
' hCryptProv specifies the crypto provider to use to compute the hash.
' It doesn't need to use a private key.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Hash the encoded content.
'
' hCryptProv specifies the crypto provider to use to compute the hash.
' It doesn't need to use a private key.
'
' Algid specifies the CAPI hash algorithm to use. If Algid is 0, then, the
'--------------------------------------------------------------------------

'+-------------------------------------------------------------------------
' Sign the "to be signed" information in the encoded signed content.
'
' hCryptProv specifies the crypto provider to use to do the signature.
' It uses the specified private key.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Encode the "to be signed" information. Sign the encoded "to be signed".
' Encode the "to be signed" and the signature.
'
' hCryptProv specifies the crypto provider to use to do the signature.
' It uses the specified private key.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verify the time validity of a certificate.
'
' Returns -1 if before NotBefore, +1 if after NotAfter and otherwise 0 for
' a valid certificate
'
' If pTimeToVerify is NULL, uses the current time.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verify the time validity of a CRL.
'
' Returns -1 if before ThisUpdate, +1 if after NextUpdate and otherwise 0 for
' a valid CRL
'
' If pTimeToVerify is NULL, uses the current time.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verify that the subject's time validity nests within the issuer's time
' validity.
'
' Returns TRUE if it nests. Otherwise, returns FALSE.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verify that the subject certificate isn't on its issuer CRL.
'
' Returns true if the certificate isn't on the CRL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Convert the CAPI AlgId to the ASN.1 Object Identifier string
'
' Returns NULL if there isn't an ObjId corresponding to the AlgId.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Convert the ASN.1 Object Identifier string to the CAPI AlgId.
'
' Returns 0 if there isn't an AlgId corresponding to the ObjId.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Find an extension identified by its Object Identifier.
'
' If found, returns pointer to the extension. Otherwise, returns NULL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Find the first attribute identified by its Object Identifier.
'
' If found, returns pointer to the attribute. Otherwise, returns NULL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Find the first CERT_RDN attribute identified by its Object Identifier in
' the name's list of Relative Distinguished Names.
'
' If found, returns pointer to the attribute. Otherwise, returns NULL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Get the intended key usage bytes from the certificate.
'
' If the certificate doesn't have any intended key usage bytes, returns FALSE
' and *pbKeyUsage is zeroed. Otherwise, returns TRUE and up through
' cbKeyUsage bytes are copied into *pbKeyUsage. Any remaining uncopied
' bytes are zeroed.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Export the public key info associated with the provider's corresponding
' private key.
'
' Calls CryptExportPublicKeyInfo with pszPublicKeyObjId = szOID_RSA_RSA,
' dwFlags = 0 and pvAuxInfo = NULL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Export the public key info associated with the provider's corresponding
' private key.
'
' Uses the dwCertEncodingType and pszPublicKeyObjId to call the
' installable CRYPT_OID_EXPORT_PUBLIC_KEY_INFO_FUNC. The called function
' has the same signature as CryptExportPublicKeyInfoEx.
'
' If unable to find an installable OID function for the pszPublicKeyObjId,
'
' The dwFlags and pvAuxInfo aren't used for szOID_RSA_RSA.
'--------------------------------------------------------------------------
Public Const CRYPT_OID_EXPORT_PUBLIC_KEY_INFO_FUNC As String = "CryptDllExportPublicKeyInfoEx"
'+-------------------------------------------------------------------------
' Convert and import the public key info into the provider and return a
' handle to the public key.
'
' Calls CryptImportPublicKeyInfoEx with aiKeyAlg = 0, dwFlags = 0 and
' pvAuxInfo = NULL.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Convert and import the public key info into the provider and return a
' handle to the public key.
'
' Uses the dwCertEncodingType and pInfo->Algorithm.pszObjId to call the
' installable CRYPT_OID_IMPORT_PUBLIC_KEY_INFO_FUNC. The called function
' has the same signature as CryptImportPublicKeyInfoEx.
'
' If unable to find an installable OID function for the pszObjId,
'
' For szOID_RSA_RSA: aiKeyAlg may be set to CALG_RSA_SIGN or CALG_RSA_KEYX.
' Defaults to CALG_RSA_KEYX. The dwFlags and pvAuxInfo aren't used.
'--------------------------------------------------------------------------
Public Const CRYPT_OID_IMPORT_PUBLIC_KEY_INFO_FUNC As String = "CryptDllImportPublicKeyInfoEx"
'+-------------------------------------------------------------------------
' Compute the hash of the encoded public key info.
'
' The public key info is encoded and then hashed.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Convert a Name Value to a null terminated char string
'
' Returns the number of characters converted including the terminating null
' character. If psz is NULL or csz is 0, returns the required size of the
'
' If psz != NULL && csz != 0, returned psz is always NULL terminated.
'
' Note: csz includes the NULL char.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Convert a Name Value to a null terminated char string
'
' Returns the number of characters converted including the terminating null
' character. If psz is NULL or csz is 0, returns the required size of the
'
' If psz != NULL && csz != 0, returned psz is always NULL terminated.
'
' Note: csz includes the NULL char.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Convert the certificate name blob to a null terminated char string.
'
' Follows the string representation of distinguished names specified in
' empty strings and don't quote strings containing consecutive spaces).
' RDN values of type CERT_RDN_ENCODED_BLOB or CERT_RDN_OCTET_STRING are
'
' The name string is formatted according to the dwStrType:
' CERT_SIMPLE_NAME_STR
' The object identifiers are discarded. CERT_RDN entries are separated
' by ", ". Multiple attributes per CERT_RDN are separated by " + ".
' For example:
' Microsoft, Joe Cool + Programmer
' CERT_OID_NAME_STR
' The object identifiers are included with a "=" separator from their
' attribute value. CERT_RDN entries are separated by ", ".
' Multiple attributes per CERT_RDN are separated by " + ". For example:
' 2.5.4.11=Microsoft, 2.5.4.3=Joe Cool + 2.5.4.12=Programmer
' CERT_X500_NAME_STR
' The object identifiers are converted to their X500 key name. Otherwise,
' same as CERT_OID_NAME_STR. If the object identifier doesn't have
' a corresponding X500 key name, then, the object identifier is used with
' a "OID." prefix. For example:
' OU=Microsoft, CN=Joe Cool + T=Programmer, OID.1.2.3.4.5.6=Unknown
'
' We quote the RDN value if it contains leading or trailing whitespace
' or one of the following characters: ",", "+", "=", """, "\n", "<", ">",
' "#" or ";". The quoting character is ". If the the RDN Value contains
' OU=" Microsoft", CN="Joe ""Cool""" + T="Programmer, Manager"
'
' CERT_NAME_STR_SEMICOLON_FLAG can be or'ed into dwStrType to replace
' the ", " separator with a "; " separator.
'
' CERT_NAME_STR_CRLF_FLAG can be or'ed into dwStrType to replace
' the ", " separator with a "\r\n" separator.
'
' CERT_NAME_STR_NO_PLUS_FLAG can be or'ed into dwStrType to replace the
' " + " separator with a single space, " ".
'
' CERT_NAME_STR_NO_QUOTING_FLAG can be or'ed into dwStrType to inhibit
' the above quoting.
'
' Returns the number of characters converted including the terminating null
' character. If psz is NULL or csz is 0, returns the required size of the
'
' If psz != NULL && csz != 0, returned psz is always NULL terminated.
'
' Note: csz includes the NULL char.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Certificate name string types
'--------------------------------------------------------------------------
Public Const CERT_SIMPLE_NAME_STR As Long = 1
Public Const CERT_OID_NAME_STR As Long = 2
Public Const CERT_X500_NAME_STR As Long = 3
'+-------------------------------------------------------------------------
' Certificate name string type flags OR'ed with the above types
'--------------------------------------------------------------------------
Public Const CERT_NAME_STR_SEMICOLON_FLAG As Long = &H40000000
Public Const CERT_NAME_STR_NO_PLUS_FLAG As Long = &H20000000
Public Const CERT_NAME_STR_NO_QUOTING_FLAG As Long = &H10000000
Public Const CERT_NAME_STR_CRLF_FLAG As Long = &H08000000
Public Const CERT_NAME_STR_COMMA_FLAG As Long = &H04000000
'+-------------------------------------------------------------------------
' Convert the null terminated X500 string to an encoded certificate name.
'
' The input string is expected to be formatted the same as the output
' from the above CertNameToStr API.
'
' The CERT_SIMPLE_NAME_STR type isn't supported. Otherwise, when dwStrType
' is set to 0, CERT_OID_NAME_STR or CERT_X500_NAME_STR, allow either a
'
' If no flags are OR'ed into dwStrType, then, allow "," or ";" as RDN
' separators and "+" as the multiple RDN value separator. Quoting is
' supported. A quote may be included in a quoted value by double quoting,
' as ascii hex and converted to a CERT_RDN_OCTET_STRING. Embedded whitespace
'
' Whitespace surrounding the keys, object identifers and values is removed.
'
' CERT_NAME_STR_COMMA_FLAG can be or'ed into dwStrType to only allow the
' "," as the RDN separator.
'
' CERT_NAME_STR_SEMICOLON_FLAG can be or'ed into dwStrType to only allow the
' ";" as the RDN separator.
'
' CERT_NAME_STR_CRLF_FLAG can be or'ed into dwStrType to only allow
' "\r" or "\n" as the RDN separator.
'
' CERT_NAME_STR_NO_PLUS_FLAG can be or'ed into dwStrType to ignore "+"
' as a separator and not allow multiple values per RDN.
'
' CERT_NAME_STR_NO_QUOTING_FLAG can be or'ed into dwStrType to inhibit
' quoting.
'
' Support the following X500 Keys:
'
' --- ----------------- -----------------
' CN szOID_COMMON_NAME Printable, T61
' L szOID_LOCALITY_NAME Printable, T61
' O szOID_ORGANIZATION_NAME Printable, T61
' OU szOID_ORGANIZATIONAL_UNIT_NAME Printable, T61
' Email szOID_RSA_emailAddr Only IA5
' C szOID_COUNTRY_NAME Only Printable
' S szOID_STATE_OR_PROVINCE_NAME Printable, T61
' ST szOID_STATE_OR_PROVINCE_NAME Printable, T61
' STREET szOID_STREET_ADDRESS Printable, T61
' T szOID_TITLE Printable, T61
' Title szOID_TITLE Printable, T61
' G szOID_GIVEN_NAME Printable, T61
' GivenName szOID_GIVEN_NAME Printable, T61
' I szOID_INITIALS Printable, T61
' Initials szOID_INITIALS Printable, T61
' SN szOID_SUR_NAME Printable, T61
' DC szOID_DOMAIN_COMPONENT Only IA5
'
' The T61 types are UTF-8 encoded.
'
' Returns TRUE if successfully parsed the input string and encoded
' the name.
'
' If the input string is detected to be invalid, *ppszError is updated
' to point to the beginning of the invalid character sequence. Otherwise,
' *ppszError is set to NULL. *ppszError is updated with a non-NULL pointer
' for the following errors:
' CRYPT_E_INVALID_X500_STRING
' CRYPT_E_INVALID_NUMERIC_STRING
' CRYPT_E_INVALID_PRINTABLE_STRING
' CRYPT_E_INVALID_IA5_STRING
'
' ppszError can be set to NULL if not interested in getting a pointer
' to the invalid character sequence.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
'--------------------------------------------------------------------------
'+=========================================================================
' Simplified Cryptographic Message Data Structures and APIs
'==========================================================================
'+-------------------------------------------------------------------------
' Conventions for the *pb and *pcb output parameters:
'
' Upon entry to the function:
' if pcb is OPTIONAL && pcb == NULL, then,
' No output is returned
' else if pb == NULL && pcb != NULL, then,
' Length only determination. No length error is
' returned.
' Output is returned. If *pcb isn't big enough a
' length error is returned. In all cases *pcb is updated
' with the actual length needed/returned.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Type definitions of the parameters used for doing the cryptographic
' operations.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Callback to get and verify the signer's certificate.
'
' handle to its cryptographic signed message's cert store.
'
' For CRYPT_E_NO_SIGNER, called with pSignerId == NULL.
'
' For a valid signer certificate, returns a pointer to a read only
' CERT_CONTEXT. The returned CERT_CONTEXT is either obtained from a
' cert store or was created via CertCreateCertificateContext. For either case,
' its freed via CertFreeCertificateContext.
'
' If a valid certificate isn't found, this callback returns NULL with
'
' The NULL implementation tries to get the Signer certificate from the
' message cert store. It doesn't verify the certificate.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' The CRYPT_SIGN_MESSAGE_PARA are used for signing messages using the
'
' Either the CERT_KEY_PROV_HANDLE_PROP_ID or CERT_KEY_PROV_INFO_PROP_ID must
' be set for each rgpSigningCert[]. Either one specifies the private
' signature key to use.
'
' If any certificates and/or CRLs are to be included in the signed message,
' then, the MsgCert and MsgCrl parameters need to be updated. If the
' rgpSigningCerts are to be included, then, they must also be in the
' rgpMsgCert array.
'
' LastError will be updated with E_INVALIDARG.
'
' pvHashAuxInfo currently isn't used and must be set to NULL.
'
' dwFlags normally is set to 0. However, if the encoded output
' is to be a CMSG_SIGNED inner content of an outer cryptographic message,
' such as a CMSG_ENVELOPED, then, the CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG
' should be set. If not set, then it would be encoded as an inner content
' type of CMSG_DATA.
'
' dwInnerContentType is normally set to 0. It needs to be set if the
' ToBeSigned input is the encoded output of another cryptographic
' message, such as, an CMSG_ENVELOPED. When set, it's one of the cryptographic
' message types, for example, CMSG_ENVELOPED.
'
' the default), then, neither dwFlags or dwInnerContentType need to be set.
'--------------------------------------------------------------------------
Public Const CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG As Long = &H1
'+-------------------------------------------------------------------------
' The CRYPT_VERIFY_MESSAGE_PARA are used to verify signed messages.
'
' hCryptProv is used to do hashing and signature verification.
'
' The dwCertEncodingType specifies the encoding type of the certificates
' and/or CRLs in the message.
'
' pfnGetSignerCertificate is called to get and verify the message signer's
' certificate.
'
' LastError will be updated with E_INVALIDARG.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' The CRYPT_ENCRYPT_MESSAGE_PARA are used for encrypting messages.
'
' hCryptProv is used to do content encryption, recipient key
' encryption, and recipient key export. Its private key
' isn't used.
'
' pvEncryptionAuxInfo currently isn't used and must be set to NULL.
'
' LastError will be updated with E_INVALIDARG.
'
' dwFlags normally is set to 0. However, if the encoded output
' is to be a CMSG_ENVELOPED inner content of an outer cryptographic message,
' such as a CMSG_SIGNED, then, the CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG
' should be set. If not set, then it would be encoded as an inner content
' type of CMSG_DATA.
'
' dwInnerContentType is normally set to 0. It needs to be set if the
' ToBeEncrypted input is the encoded output of another cryptographic
' message, such as, an CMSG_SIGNED. When set, it's one of the cryptographic
' message types, for example, CMSG_SIGNED.
'
' the default), then, neither dwFlags or dwInnerContentType need to be set.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' The CRYPT_DECRYPT_MESSAGE_PARA are used for decrypting messages.
'
' The CertContext to use for decrypting a message is obtained from one
' of the specified cert stores. An encrypted message can have one or
' and SerialNumber). The cert stores are searched to find the CertContext
' corresponding to the CertId.
'
' Only CertContexts in the store with either
' the CERT_KEY_PROV_HANDLE_PROP_ID or CERT_KEY_PROV_INFO_PROP_ID set
' can be used. Either property specifies the private exchange key to use.
'
' LastError will be updated with E_INVALIDARG.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' The CRYPT_HASH_MESSAGE_PARA are used for hashing or unhashing
' messages.
'
' hCryptProv is used to compute the hash.
'
' pvHashAuxInfo currently isn't used and must be set to NULL.
'
' LastError will be updated with E_INVALIDARG.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' The CRYPT_KEY_SIGN_MESSAGE_PARA are used for signing messages until a
' certificate has been created for the signature key.
'
' pvHashAuxInfo currently isn't used and must be set to NULL.
'
' LastError will be updated with E_INVALIDARG.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' The CRYPT_KEY_VERIFY_MESSAGE_PARA are used to verify signed messages without
' a certificate for the signer.
'
' Normally used until a certificate has been created for the key.
'
' hCryptProv is used to do hashing and signature verification.
'
' LastError will be updated with E_INVALIDARG.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Sign the message.
'
' If fDetachedSignature is TRUE, the "to be signed" content isn't included
' in the encoded signed blob.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verify a signed message.
'
' If pbDecoded == NULL, then, *pcbDecoded is implicitly set to 0 on input.
' For *pcbDecoded == 0 && ppSignerCert == NULL on input, the signer isn't
' verified.
'
' A message might have more than one signer. Set dwSignerIndex to iterate
' through all the signers. dwSignerIndex == 0 selects the first signer.
'
' pVerifyPara's pfnGetSignerCertificate is called to get the signer's
' certificate.
'
' For a verified signer and message, *ppSignerCert is updated
' with the CertContext of the signer. It must be freed by calling
' CertFreeCertificateContext. Otherwise, *ppSignerCert is set to NULL.
'
' ppSignerCert can be NULL, indicating the caller isn't interested
' in getting the CertContext of the signer.
'
' pcbDecoded can be NULL, indicating the caller isn't interested in getting
' the decoded content. Furthermore, if the message doesn't contain any
' content or signers, then, pcbDecoded must be set to NULL, to allow the
' pVerifyPara->pfnGetCertificate to be called. Normally, this would be
' the case when the signed message contains only certficates and CRLs.
' If pcbDecoded is NULL and the message doesn't have the indicated signer,
' pfnGetCertificate is called with pSignerId set to NULL.
'
' If the message doesn't contain any signers || dwSignerIndex > message's
' SignerCount, then, an error is returned with LastError set to
' CRYPT_E_NO_SIGNER. Also, for CRYPT_E_NO_SIGNER, pfnGetSignerCertificate
' is still called with pSignerId set to NULL.
'
' Note, an alternative way to get the certificates and CRLs from a
' signed message is to call CryptGetMessageCertificates.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Returns the count of signers in the signed message. For no signers, returns
' 0. For an error returns -1 with LastError updated accordingly.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Returns the cert store containing the message's certs and CRLs.
' For an error, returns NULL with LastError updated.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' The "to be signed" content is passed in separately. No
' decoded output. Otherwise, identical to CryptVerifyMessageSignature.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Decrypts the message.
'
' If pbDecrypted == NULL, then, *pcbDecrypted is implicitly set to 0 on input.
' For *pcbDecrypted == 0 && ppXchgCert == NULL on input, the message isn't
' decrypted.
'
' For a successfully decrypted message, *ppXchgCert is updated
' with the CertContext used to decrypt. It must be freed by calling
' CertStoreFreeCert. Otherwise, *ppXchgCert is set to NULL.
'
' ppXchgCert can be NULL, indicating the caller isn't interested
' in getting the CertContext used to decrypt.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' followed with a CryptEncryptMessage.
'
' Note: this isn't the CMSG_SIGNED_AND_ENVELOPED. Its a CMSG_SIGNED
' inside of an CMSG_ENVELOPED.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Decrypts the message and verifies the signer. Does a CryptDecryptMessage
' followed with a CryptVerifyMessageSignature.
'
' If pbDecrypted == NULL, then, *pcbDecrypted is implicitly set to 0 on input.
' For *pcbDecrypted == 0 && ppSignerCert == NULL on input, the signer isn't
' verified.
'
' A message might have more than one signer. Set dwSignerIndex to iterate
' through all the signers. dwSignerIndex == 0 selects the first signer.
'
' The pVerifyPara's VerifySignerPolicy is called to verify the signer's
' certificate.
'
' For a successfully decrypted and verified message, *ppXchgCert and
' *ppSignerCert are updated. They must be freed by calling
' CertStoreFreeCert. Otherwise, they are set to NULL.
'
' ppXchgCert and/or ppSignerCert can be NULL, indicating the
' caller isn't interested in getting the CertContext.
'
' Note: this isn't the CMSG_SIGNED_AND_ENVELOPED. Its a CMSG_SIGNED
' inside of an CMSG_ENVELOPED.
'
' The message always needs to be decrypted to allow access to the
' signed message. Therefore, if ppXchgCert != NULL, its always updated.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Decodes a cryptographic message which may be one of the following types:
' CMSG_DATA
' CMSG_SIGNED
' CMSG_ENVELOPED
' CMSG_SIGNED_AND_ENVELOPED
' CMSG_HASHED
'
' dwMsgTypeFlags specifies the set of allowable messages. For example, to
' decode either SIGNED or ENVELOPED messages, set dwMsgTypeFlags to:
' CMSG_SIGNED_FLAG | CMSG_ENVELOPED_FLAG.
'
' dwProvInnerContentType is only applicable when processing nested
' crytographic messages. When processing an outer crytographic message
' it must be set to 0. When decoding a nested cryptographic message
' its the dwInnerContentType returned by a previous CryptDecodeMessage
' of the outer message. The InnerContentType can be any of the CMSG types,
' for example, CMSG_DATA, CMSG_SIGNED, ...
'
' The optional *pdwMsgType is updated with the type of message.
'
' The optional *pdwInnerContentType is updated with the type of the inner
' message. Unless there is cryptographic message nesting, CMSG_DATA
' is returned.
'
' For CMSG_DATA: returns decoded content.
' For CMSG_SIGNED: same as CryptVerifyMessageSignature.
' For CMSG_ENVELOPED: same as CryptDecryptMessage.
' For CMSG_SIGNED_AND_ENVELOPED: same as CryptDecryptMessage plus
' CryptVerifyMessageSignature.
' For CMSG_HASHED: verifies the hash and returns decoded content.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Hash the message.
'
' If fDetachedHash is TRUE, only the ComputedHash is encoded in the
' pbHashedBlob. Otherwise, both the ToBeHashed and ComputedHash
' are encoded.
'
' pcbHashedBlob or pcbComputedHash can be NULL, indicating the caller
' isn't interested in getting the output.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verify a hashed message.
'
' pcbToBeHashed or pcbComputedHash can be NULL,
' indicating the caller isn't interested in getting the output.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verify a hashed message containing a detached hash.
' The "to be hashed" content is passed in separately. No
' decoded output. Otherwise, identical to CryptVerifyMessageHash.
'
' pcbComputedHash can be NULL, indicating the caller isn't interested
' in getting the output.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Sign the message using the provider's private key specified in the
' parameters. A dummy SignerId is created and stored in the message.
'
' Normally used until a certificate has been created for the key.
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Verify a signed message using the specified public key info.
'
' Normally called by a CA until it has created a certificate for the
' key.
'
' pPublicKeyInfo contains the public key to use to verify the signed
' content may contain the PublicKeyInfo).
'
' pcbDecoded can be NULL, indicating the caller isn't interested
' in getting the decoded content.
'--------------------------------------------------------------------------
'+=========================================================================
' System Certificate Store Data Structures and APIs
'==========================================================================
'+-------------------------------------------------------------------------
' Get a system certificate store based on a subsystem protocol.

'
' Current examples of subsystems protocols are:
' "MY" Cert Store hold certs with associated Private Keys
' "CA" Certifying Authority certs
' "ROOT" Root Certs
' "SPC" Software publisher certs
'
'
' If hProv is NULL the default provider "1" is opened for you.
' When the store is closed the provider is release. Otherwise
' if hProv is not NULL, no provider is created or released.
'
' The returned Cert Store can be searched for an appropriate Cert
'
' When done, the cert store should be closed using CertStoreClose
'--------------------------------------------------------------------------
'+-------------------------------------------------------------------------
' Find all certificate chains tying the given issuer name to any certificate
' that the current user has a private key for.
'
' If no certificate chain is found, FALSE is returned with LastError set
' to CRYPT_E_NOT_FOUND and the counts zeroed.
'
' IE 3.0 ASSUMPTION:
' The client certificates are in the "My" system store. The issuer
' cerificates may be in the "Root", "CA" or "My" system stores.
'--------------------------------------------------------------------------
' WINCRYPT32API This is not exported by crypt32, it is exported by softpub
'+--------------------------------------------------------------------------
'
' Microsoft Windows
'
' File: cs.h
'
' Contents: Cert Server common definitions
'
' History: 25-Jul-96 vich created
'
'---------------------------------------------------------------------------
Public Const VR_PENDING As Long = 0
Public Const VR_INSTANT_OK As Long = 1
Public Const VR_INSTANT_BAD As Long = 2
' Certificate types:
Public Const CERT_TYPE_NONE As Long = 0
Public Const CERT_TYPE_X509 As Long = 1
Public Const CERT_TYPE_SDSI As Long = 2
Public Const CERT_TYPE_PGP As Long = 3
' Request types:
Public Const REQ_TYPE_NONE As Long = 0
Public Const REQ_TYPE_PKCS10 As Long = 1
Public Const REQ_TYPE_KEYGEN As Long = 2
' RegisterNewRequest is called to deliver a new request to the certificate
' server. dwType indicates the type of request that has been received.
' The following values are valid:
Public Const RNR_T_UNK As Long = 0
Public Const RNR_T_PKCS10 As Long = 1
Public Const RNR_T_KEYGEN As Long = 2
Public Const RNR_T_CUSTOM As Long = 3
' Transport Info:
' The transport info structure is used several places in the certificate
' server to describe how request arrived, or how a certificate or CRL is to be
' sent. The structure can define an address in steps of precision. It can be
' just a type of transport, a specific transport mechanism, a transport
' address, or a specific authenticated user at an address.
' Address Types:
Public Const ADR_TYPE As Long = &H00000001
Public Const ADR_STYPE As Long = &H00000002
Public Const ADR_ADDR As Long = &H00000004
Public Const ADR_USER As Long = &H00000008
Public Const ADR_AUTH As Long = &H00000010
Public Const ADR_TIME As Long = &H00000020
' Transport Types:
Public Const TTYPE_FREE As Long = 0
Public Const TTYPE_EMAIL As Long = 1
Public Const TTYPE_DNS As Long = 2
' EMAIL subtypes:
Public Const TSUB_MAIL_GENERIC As Long = 1
Public Const TSUB_MAIL_PEM As Long = 2
Public Const TSUB_MAIL_EXCHSEC As Long = 3
Public Const TSUB_MAIL_PGP As Long = 4
' DNS subtypes:
Public Const TSUB_DNS_FTP As Long = 1
Public Const TSUB_DNS_HTTP As Long = 2
Public Const TSUB_DNS_HTTPS As Long = 3
' Authentication Types:
Public Const AAT_CERT As Long = 1
Public Const AAT_NTLM As Long = 2
Public Const AAT_SICILY As Long = 3
'
' VOID
' PLIST_ENTRY ListHead
' );
'
'
' VOID
' PLIST_ENTRY ListHead,
' PLIST_ENTRY Entry
' );
'
'
' VOID
' PLIST_ENTRY Entry
' );
'
'+--------------------------------------------------------------------------
'
' Microsoft Windows
'
' File: csprop.h
'
' Contents: Cert Server Property interfaces
'
' History: 31-Jul-96 vich created
'
'---------------------------------------------------------------------------
'+--------------------------------------------------------------------------
' Name properties:
Public Const wszPROPDISTINGUISHEDNAME As String = "DistinguishedName"
Public Const wszPROPRAWNAME As String = "RawName"
Public Const wszPROPNAMETYPE As String = "NameType"
Public Const wszPROPCOUNTRY As String = "Country"
Public Const wszPROPORGANIZATION As String = "Organization"
Public Const wszPROPORGUNIT As String = "OrgUnit"
Public Const wszPROPCOMMONNAME As String = "CommonName"
Public Const wszPROPLOCALITY As String = "Locality"
Public Const wszPROPSTATE As String = "State"
Public Const wszPROPTITLE As String = "Title"
Public Const wszPROPGIVENNAME As String = "GivenName"
Public Const wszPROPINITIALS As String = "Initials"
Public Const wszPROPSURNAME As String = "SurName"
Public Const wszPROPDOMAINCOMPONENT As String = "DomainComponent"
Public Const wszPROPEMAIL As String = "EMail"
'+--------------------------------------------------------------------------
' Subject Name properties:
Public Const wszPROPSUBJECTDOT As String = "Subject."
'+--------------------------------------------------------------------------
' Issuer Name properties:
Public Const wszPROPISSUERDOT As String = "Issuer."
'+--------------------------------------------------------------------------
' For mapping request attribute names to internal property names:
' Map to wszPROPSUBJECTCOMMONNAME:
Public Const wszATTRCOMMONNAME1 As String = "CN"
Public Const wszATTRCOMMONNAME2 As String = "CommonName"
' Map to wszPROPSUBJECTLOCALITY:
Public Const wszATTRLOCALITY1 As String = ""
Public Const wszATTRLOCALITY2 As String = "Locality"
' Map to wszPROPSUBJECTSTATE:
Public Const wszATTRSTATE1 As String = "S"
Public Const wszATTRSTATE2 As String = "State"
' Map to wszPROPSUBJECTCOUNTRY:
Public Const wszATTRCOUNTRY1 As String = "C"
Public Const wszATTRCOUNTRY2 As String = "Country"
' Map to wszPROPSUBJECTORGANIZATION:
Public Const wszATTRORG1 As String = "O"
Public Const wszATTRORG2 As String = "Org"
Public Const wszATTRORG3 As String = "Organization"
' Map to wszPROPSUBJECTORGUNIT:
Public Const wszATTRORGUNIT1 As String = "OU"
Public Const wszATTRORGUNIT2 As String = "OrgUnit"
Public Const wszATTRORGUNIT3 As String = "OrganizationUnit"
Public Const wszATTRORGUNIT4 As String = "OrganizationalUnit"
' Map to wszPROPSUBJECTDOMAINCOMPONENT:
Public Const wszATTRDOMAINCOMPONENT1 As String = "DC"
Public Const wszATTRDOMAINCOMPONENT2 As String = "DomainComponent"
' Map to wszPROPSUBJECTEMAIL:
Public Const wszATTREMAIL1 As String = "EMail"
'+--------------------------------------------------------------------------
' Request properties:
Public Const wszPROPREQUESTREQUESTID As String = "RequestID"
Public Const wszPROPREQUESTRAWREQUEST As String = "RawRequest"
Public Const wszPROPREQUESTATTRIBUTES As String = "RequestAttributes"
Public Const wszPROPREQUESTTYPE As String = "RequestType"
Public Const wszPROPREQUESTFLAGS As String = "RequestFlags"
Public Const wszPROPREQUESTSTATUS As String = "Status"
Public Const wszPROPREQUESTSTATUSCODE As String = "StatusCode"
Public Const wszPROPREQUESTDISPOSITION As String = "Disposition"
Public Const wszPROPREQUESTDISPOSITIONMESSAGE As String = "DispositionMessage"
Public Const wszPROPREQUESTSUBMITTEDWHEN As String = "SubmittedWhen"
Public Const wszPROPREQUESTRESOLVEDWHEN As String = "ResolvedWhen"
Public Const wszPROPREQUESTREVOKEDWHEN As String = "RevokedWhen"
Public Const wszPROPREQUESTREVOKEDEFFECTIVEWHEN As String = "RevokedEffectiveWhen"
Public Const wszPROPREQUESTREVOKEDREASON As String = "RevokedReason"
Public Const wszPROPREQUESTSUBJECTNAMEID As String = "SubjectNameID"
Public Const wszPROPREQUESTERNAME As String = "RequesterName"
Public Const wszPROPREQUESTERADDRESS As String = "RequesterAddress"
'+--------------------------------------------------------------------------
' Request attribute properties:
Public Const wszPROPCHALLENGE As String = "Challenge"
Public Const wszPROPEXPECTEDCHALLENGE As String = "ExpectedChallenge"
'+--------------------------------------------------------------------------
' Certificate properties:
Public Const wszPROPCERTIFICATEREQUESTID As String = "RequestID"
Public Const wszPROPRAWCERTIFICATE As String = "RawCertificate"
Public Const wszPROPCERTIFICATETYPE As String = "CertificateType"
Public Const wszPROPCERTIFICATESERIALNUMBER As String = "SerialNumber"
Public Const wszPROPCERTIFICATEISSUERNAMEID As String = "IssuerNameID"
Public Const wszPROPCERTIFICATESUBJECTNAMEID As String = "SubjectNameID"
Public Const wszPROPCERTIFICATENOTBEFOREDATE As String = "NotBefore"
Public Const wszPROPCERTIFICATENOTAFTERDATE As String = "NotAfter"
Public Const wszPROPCERTIFICATERAWPUBLICKEY As String = "RawPublicKey"
Public Const wszPROPCERTIFICATEPUBLICKEYALGORITHM As String = "PublicKeyAlgorithm"
'+--------------------------------------------------------------------------
' Certificate extension properties:
Public Const wszPROPCERTIFICATEEXTENSIONFLAGS As String = "Flags"
Public Const wszPROPCERTIFICATEEXTENSIONVALUE As String = "Value"
Public Const EXTENSION_CRITICAL_FLAG As Long = &H00000001
Public Const EXTENSION_DISABLE_FLAG As Long = &H00000002
Public Const EXTENSION_POLICY_MASK As Long = &H0000ffff
Public Const EXTENSION_ORIGIN_REQUEST As Long = &H00010000
Public Const EXTENSION_ORIGIN_POLICY As Long = &H00020000
Public Const EXTENSION_ORIGIN_ADMIN As Long = &H00030000
Public Const EXTENSION_ORIGIN_SERVER As Long = &H00040000
Public Const EXTENSION_ORIGIN_MASK As Long = &H000f0000
'+--------------------------------------------------------------------------
' GetProperty/SetProperty Flags:
'
' Choose one Type
Public Const PROPTYPE_LONG As Long = &H00000001
Public Const PROPTYPE_DATE As Long = &H00000002
Public Const PROPTYPE_BINARY As Long = &H00000003
Public Const PROPTYPE_STRING As Long = &H00000004
Public Const PROPTYPE_ANSI As Long = &H00000005
Public Const PROPTYPE_MASK As Long = &H000000ff
' Choose one Caller:
Public Const PROPCALLER_SERVER As Long = &H00000100
Public Const PROPCALLER_POLICY As Long = &H00000200
Public Const PROPCALLER_EXIT As Long = &H00000300
Public Const PROPCALLER_ADMIN As Long = &H00000400
Public Const PROPCALLER_MASK As Long = &H00000f00
' Choose one Table:
Public Const PROPTABLE_REQUEST As Long = &H00001000
Public Const PROPTABLE_CERTIFICATE As Long = &H00002000
Public Const PROPTABLE_EXTENSIONS As Long = &H00003000
Public Const PROPTABLE_ATTRIBUTES As Long = &H00004000
Public Const PROPTABLE_MASK As Long = &H0000f000
' Internal use only
Public Const PROPTABLE_EXTENSIONFLAGS As Long = &H00010000
Public Const PROPTABLE_EXTENSIONVALUE As Long = &H00020000
Public Const cchSERIALNUMBERMAX As Long = 64
Public Const cchOBJECTIDMAX As Long = 31
Public Const cchDISTINGUISHEDNAMEMAX As Long = 254
Public Const cchCOUNTRYNAMEMAX As Long = 2
' Request Status property values:
Public Const REQSTATUS_ACTIVE As Long = 1
Public Const REQSTATUS_ACCEPTED As Long = 2
Public Const REQSTATUS_DENIED As Long = 3
Public Const REQSTATUS_PENDING As Long = 4
Public Const REQSTATUS_ERROR As Long = 5
' CertIF property callback support:
' CertIF property enumeration callback support:
Public Const CIE_OBJECTID As Long = &H00000001
Public Const CIE_TABLE_EXTENSIONS As Long = &H00000010
Public Const CIE_TABLE_ATTRIBUTES As Long = &H00000020
Public Const CIE_TABLE_MASK As Long = &H000000f0
'+--------------------------------------------------------------------------
'
' Microsoft Windows
'
' File: certreq.h
'
' Contents: ICertRequest definitions
'
' History: 03-Jan-97 vich created
'
'---------------------------------------------------------------------------
'+--------------------------------------------------------------------------
' Known request Attribute names and Value strings
' RequestType attribute name:
Public Const wszCERT_TYPE As String = "RequestType"
' RequestType attribute values:
' Not specified:
Public Const wszCERT_TYPE_CLIENT As String = "Client"
Public Const wszCERT_TYPE_SERVER As String = "Server"
Public Const wszCERT_TYPE_CODESIGN As String = "CodeSign"
Public Const wszCERT_TYPE_CUSTOMER As String = "SetCustomer"
Public Const wszCERT_TYPE_MERCHANT As String = "SetMerchant"
Public Const wszCERT_TYPE_PAYMENT As String = "SetPayment"
' Version attribute name:
Public Const wszCERT_VERSION As String = "Version"
' Version attribute values:
' Not specified:
Public Const wszCERT_VERSION_1 As String = "1"
Public Const wszCERT_VERSION_2 As String = "2"
Public Const wszCERT_VERSION_3 As String = "3"


README.TXT

Cryptography Filer

SUMMARY
=======

The CFiler sample is a file encryptor that uses the Microsoft Cryptography
API (CAPI). Its interface is an adaptation of the FILER sample. CFiler
demonstrates a large part of the capabilities of CAPI, including encryption,
digital signature, and password-based key generation.

CFiler's interface contains two list boxes: the directory list box and the
file list box. Users navigate about their directory tree by double-clicking
on the directory list box, which is located on the left of CFiler's client
area. Double-clicking on an entry in the directory list box once causes the
directory to be expanded, with all its subdirectories displayed indented
below the entry. Double-clicking on the entry a second time causes the entry
to be collapsed. Users can perform cryptographic operations on files and
directories by selecting them in the file list box and selecting operations
from the menu. When a user double-clicks on an item in the file list box,
CFiler encrypts the file and displays its filename in gray text. Note: The
plaintext file is deleted. A second double-click causes CFiler to decrypt
the file and display its filename in its original color. Users sign files by
first selecting them in the file list box and then selecting Sign from the
File menu. Signed files appear in red text. Users can verify the signature
of a file by selecting the file in the file list box and choosing Verify from
the file menu. An encrypted file that has been signed appears in light red
text. When users encrypt and then sign files, they know that adversaries
have not encrypted the files. Users can select the algorithms they wish to
use for encryption and digital signature from the Options menu.

The first time a user runs CFiler, the application creates a key called fnKey
and stores the key in the registry. From then on, when the user starts
CFiler, the application access fnKey from the registry. CFiler uses fnKey to
encrypt file and directory names. When a user chooses to encrypt a file,
CFiler generates a session key with which to encrypt the file, exports the
key, generates a random filename with a .CRP extension, creates a hidden
file with this name, encrypts the name of the plaintext file with fnKey,
stores the encrypted filename to the .CRP file, stores the exported key to
the .CRP file, encrypts the data with the session key, stores the encrypted
content to the .CRP file, and deletes the plaintext file. When the user
chooses to encrypt a directory, CFiler encrypts the name of the directory
with fnKey, stores the data in a file called dirinfo in the directory,
generates a random name with a .CRP extension, renames the directory
using this name, hides the directory, encrypts all the files in the
directory, and recursively encrypts all subdirectories. If the fnKey is lost
or tampered with, CFiler displays the file in the file list box as RecoverMe
in gray text. Although the user can recover the original file, the original
filename is lost when fnKey is lost.

The Password item under the Options menu prompts the user for a password.
CFiler generates a session key with this password the next time the user
encrypts a file. When the user wishes to decrypt that file, the application
again prompts the user for the password.

CFiler never displays the random file and directory names it generates.
CFiler only displays the original names. CFiler accomplishes this by
maintaining two string tables: one for the directory list box and one for the
file list box. Each entry in the string table has a hidden and displayed
field. The hidden field holds the pathname as it appears on the disk. The
displayed field holds the pathname as it appeared on the disk before
encryption.

CFiler stores all signatures of files along with the ALG_ID's of the hashing
algorithms used to generate the signatures as separate, hidden files in the
hidden directory sig off the root on the same drive as the system directory.
This directory is hidden by default. Users may choose to hide or unhide this
directory and its contents. The filenames of these files are SHA hashes of
the full pathnames of the files. It is necessary to convert these 20-byte
hashes before using them as filenames since there are only 2^6 rather than
2^8 legal characters for filenames. By choosing 64 (2^6) characters for
filenames (A-Z, a-z, 0-9, +, and _), it is possible to convert 3 bytes of
the hash, containing 24 = 8 * 3 bits of data, to 4 bytes of the filename,
also containing 24 = 6 * 4 bits of data. CFiler adds 4 null bytes to the hash
to have 24 bytes with which to perform the conversion. CFiler knows that a
file has a signature if the hash of its full pathname is one of the files in
the sig directory. To increase efficiency, CFiler creates in memory a table
of the signature files when filling the file list box.

The default encryption algorithm is RC2, which is not fixed. For password-
based key generation, MD4 is the hashing algorithm, and it cannot be changed.
The default hashing algorithm for generating signature files (which the user
can change) is MD4. The algorithm used to generate fnKey is RC4. The
algorithm used to hash pathnames of files in order to generate signature
filenames is SHA, which is fixed. It is necessary to #define WIN95 when
compiling under Windows 95. Making CFiler a Unicode application simply
requires a #define UNICODE.

MORE INFORMATION
================

Encrypted files are structured  in the following way:

 1. Filename encrypted with fnKey (RC4). - 256 * sizeof(TCHAR) bytes.
 2. Exported key blob length - sizeof(DWORD) bytes. This is zero if the user
    selected a password-based key generation algorithm.
 3. Key blob - (key blob length bytes) (0 bytes if generated from password).
 4. Encrypted content.

Menus
-----

  - File: Contains items which perform cryptographic operations on files.
  - View: Contains items which change the view of the file and directory
    list boxes.
  - Drives: Contains a list of available drives.
  - Options: Contains items which set options for the cryptography operations.
  - Help: Contains information for the user to obtain help and version
    information.

File Menu
---------

  - Encrypt/Decrypt: If the selected file in the file list box is plaintext,
    this menu item causes the application to encrypt the file. If the
    selected item is encrypted, this menu item causes the application to
    decrypt the file. During encryption, the plaintext file is deleted.
    During decryption, the encrypted file is deleted.

  - Sign: Creates a separate digital signature of the selected file in the
    file list box. This file is stored in the directory sig off the root on
    the same drive as the system directory. If a signature file already
    exists, the application asks users if they want to delete the signature
    file and replace it with a new signature file.

  - Verify: Verifies the signature of the file selected in the file list box.

  - Exit: Terminates the application.

View Menu
---------

  - Refresh: Refreshes the view of the current drive to that of the root.

  - Expand Tree: Recursively expands the directory tree in the directory
    list box.

  - Swap Sides: Swaps the file and directory list boxes.

Drives Menu
-----------

Lists available drives. When a user selects one of the items in this menu,
the application switches to that drive.

Options Menu
------------

  - Choose Encryption Algorithm: Prompts the user with a dialog box with
    radio buttons for each algorithm for generating session keys.

  - Enter Password: Prompts the user with a dialog box with a field for a
    password with which to generate session keys.

  - Choose Signature Algorithm: Prompts the user with a dialog box containing
    radio buttons for various algorithms with which to create digital
    signatures.

  - Hide Signatures (either checked or unchecked): Toggles the hidden or
    shown status of the directory containing digital signatures as well as
    the hidden or shown status of the signatures themselves.

Help Menu
---------

  - CFiler Help: Loads the CFiler help file.

  - About CFiler: Shows the CFiler version information.


CFILER.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// CFILER.C
#include "cfiler.h"
#include "time.h"

HANDLE  ghModule;
HANDLE  ghDrvThread = NULL;
HWND    ghwndMain = NULL;
HWND    ghwndDrives;
HWND    ghwndDrv;
HWND    ghFocusWnd;
HFONT   ghFont;
HMENUghMenu;
BOOL                gfDrvWndOrient = SIDE_BY_SIDE,
                    gfKeepCommandWin = FALSE;
DRVCHILDINFO        gDrvChildInfo;
LPDINFO             glpDrives = (LPDINFO)0;
CRITICAL_SECTION    gDrvCS;

extern LPTSTR lpDriveStrings;
extern ALG_IDAlgidEncrypt;
extern ALG_IDAlgidSign;
extern CHARsPass[BUF_SIZE];
extern TCHARszPassName[BUF_SIZE];

int WINAPI WinMain(HINSTANCE hInstance,
   HINSTANCE hPrevInstance,
   LPSTR lpCmdLine,
   INT nCmdShow) {
    MSG    msg;
    HANDLE hAccel;

    ghModule = hInstance;

    if (!InitializeApp()) {
        ErrorMsg(TEXT("WinMain: InitializeApp failed."));
        return 0;
    }

    ShowWindow(ghwndMain, nCmdShow);

    if (!(hAccel = LoadAccelerators (ghModule, MAKEINTRESOURCE(ACCEL_ID))))
        ErrorMsg(TEXT("WinMain: LoadAccelerators failed."));

    while (GetMessage(&msg, NULL, 0, 0)) {
        if( !TranslateAccelerator(ghwndMain, hAccel, &msg) ) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return 1;

    UNREFERENCED_PARAMETER(lpCmdLine);
    UNREFERENCED_PARAMETER(hPrevInstance);
}

BOOL InitializeApp(void)
{
    WNDCLASS wc;
HWND FirsthWnd, FirstChildhWnd;

if (FirsthWnd = FindWindow("CRYPTFILERClass", NULL))
{
   // Found another running application with the same class name.
   // Therefore, one instance is already running.

   FirstChildhWnd = GetLastActivePopup(FirsthWnd);
   BringWindowToTop(FirsthWnd);         // bring main window to top

   if (FirsthWnd != FirstChildhWnd)
      BringWindowToTop(FirstChildhWnd); // a pop-up window is active
                                        // bring it to the top too

   return FALSE;                        // do not run second instance
}

    srand(time(0));

    lstrcpy(szPassName, TEXT("\0"));

    wc.style            = 0;
    wc.lpfnWndProc      = (WNDPROC)MainWndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra   = 0;
    wc.hInstance        = ghModule;
    wc.hIcon            = LoadIcon(ghModule, MAKEINTRESOURCE(UI_CRYPTFILERICON));
    wc.hCursor          = 0;
    wc.hbrBackground    = (HBRUSH)(COLOR_APPWORKSPACE);
    wc.lpszMenuName     = TEXT("CRYPTFILERMenu");
    wc.lpszClassName    = TEXT("CRYPTFILERClass");

    if (!RegisterClass(&wc)) {
ErrorMsg(TEXT("InitializeApp: RegisterClass failed."));
    return FALSE;
}

    wc.lpfnWndProc  = DrvWndProc;
    wc.hIcon        = NULL;
    wc.lpszMenuName = NULL;
    wc.lpszClassName    = TEXT("DrvClass");

    if (!RegisterClass(&wc)) {
        ErrorMsg(TEXT("InitializeApp: RegisterClass failed."));
        return FALSE;
    }

    wc.style        = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc  = DriveBarProc;
    wc.hbrBackground    = (HBRUSH)(COLOR_BTNSHADOW);
    wc.lpszClassName    = TEXT("DriveBarClass");

    if (!RegisterClass(&wc)) {
        ErrorMsg(TEXT("InitializeApp: RegisterClass failed."));
        return FALSE;
}

    wc.style        = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc  = TextWndProc;
    wc.hbrBackground    = (HBRUSH)(COLOR_INACTIVECAPTION);
    wc.lpszClassName    = TEXT("TextClass");

    if (!RegisterClass(&wc)) {
        ErrorMsg(TEXT("InitializeApp: RegisterClass failed."));
        return FALSE;
}

    ghMenu = LoadMenu(ghModule, TEXT("CRYPTFILERMenu"));

    if (!ghMenu)
return FALSE;

    AlgidEncrypt = CALG_RC2;
AlgidSign = CALG_MD4;

ghwndMain = CreateWindow(TEXT("CRYPTFILERClass"),
                             TEXT("CRYPTFILER"),
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT,
                             CW_USEDEFAULT,
                             MAIN_WIDTH,
                             MAIN_HEIGHT,
                             HWND_DESKTOP,
                             ghMenu,
                             ghModule,
                             NULL);

    if (!ghwndMain) {
ErrorMsg(TEXT("InitializeApp: CreateWindow failed."));
return FALSE;
}

if (!MakeHashDirectory()) {
ErrorMsg(TEXT("InitializeApp: MakeHashDirectory failed."));
return FALSE;
}

return TRUE;
}

LRESULT WINAPI MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    INT bRet;

    switch (message) {
    case WM_CREATE: {
        LOGFONT    lf;
        HDC        hDC;
        HGDIOBJ    hOldFont;
HGDIOBJ   hFont;
        TEXTMETRIC tm;
        DWORD dwThreadID;
DWORD dwLastError = GetLastError();
        LPCINFO lpCInfo;

        //
        // Initialize drive list and Set Directory critical sections.
        //
        InitializeCriticalSection(&gDrvCS);

        ghDrvThread = CreateThread(NULL, 0,
                                   (LPTHREAD_START_ROUTINE)EnumDrives,
                                   (LPVOID)&glpDrives,
                                   0, &dwThreadID);

if (!ghDrvThread) {
ErrorMsg(TEXT("MainWndProc: CreateThread failed."));
return 0;
}

        //
        // Compute default application font by creating a bold version
        //   of the system default icon font.
        //
        if (!SystemParametersInfo(SPI_GETICONTITLELOGFONT,
          sizeof(lf),
          (PVOID) &lf,
          FALSE)) {

ErrorMsg(TEXT("MainWndProc: SystemParametersInfo failed."));
return 0;
}

        hDC = GetDC(hwnd);

        if (!hDC) {
ErrorMsg(TEXT("MainWndProc: GetDC failed."));
return 0;
}

        //
        // this is the height for 8 point size font in pixels.
        //  (1 point = 1/72 in.)
        //
        lf.lfHeight = 8 * GetDeviceCaps(hDC, LOGPIXELSY) / 72;
        lf.lfWeight = BOLD_FONT;

        ghFont = CreateFontIndirect(&lf);
if (!ghFont) {
ErrorMsg(TEXT("MainWndProc: CreateFontIndirect failed."));
return 0;
}

        hOldFont = SelectObject(hDC, ghFont);

        if (!hOldFont || (HGDIOBJ)hOldFont == (HGDIOBJ)GDI_ERROR) {
ErrorMsg(TEXT("MainWndProc: SelectObject failed."));
return 0;
}

        if (!GetTextMetrics(hDC, &tm)) {
ErrorMsg(TEXT("MainWndProc: GetTextMetrics failed."));
return 0;
}

        if (hOldFont) {
hFont = SelectObject(hDC, hOldFont);
 if (!hFont || (HGDIOBJ)hFont == (HGDIOBJ)GDI_ERROR) {
 ErrorMsg(TEXT("MainWndProc: SelectObject failed."));
 return 0;
 }
 }

        if (!ReleaseDC(hwnd, hDC)) {
ErrorMsg(TEXT("MainWndProc: ReleaseDC failed.\r\n"));
return 0;
}

        //
        // Create Drive windows
        //
        gDrvChildInfo.hParent = hwnd;

  ghwndDrv = CreateWindow(TEXT("DrvClass"), NULL,
                                 WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
                                 0, 0, 0, 0,
                                 hwnd, (HMENU) 1, ghModule,
                                 (LPVOID)&gDrvChildInfo);

if (!ghwndDrv) {
ErrorMsg(TEXT("MainWndProc: CreateWindow failed."));
dwLastError = GetLastError();
return 0;
}

        ghwndDrv = ghwndDrv;

        //
        // Set initial focus to Drive Child 1's Directory listbox.
        //
        lpCInfo = (LPCINFO)GetWindowLong(ghwndDrv, GWL_USERDATA);

        if (!lpCInfo) {
        ErrorMsg(TEXT("MainWndProc: WM_CREATE: lpCInfo is NULL."));
        return 0;
        }

        ghFocusWnd = lpCInfo->hDirLB;

        if (!ghFocusWnd) {
        ErrorMsg(TEXT("MainWndProc: GetWindowLong failed."));
return 0;
}

        // Create DriveBar, FunctionBar and Command windows

        ghwndDrives = CreateWindow(TEXT("DriveBarClass"), NULL,
                                    WS_CHILD | WS_VISIBLE | WS_BORDER,
                                   0, 0, 0, 0,
                                   hwnd, (HMENU) 3, ghModule,
                                   (LPVOID)NULL);

        if (!ghwndDrives) {
        ErrorMsg(TEXT("MainWndProc: CreateWindow failed."));
        return 0;
        }

        UpdateDrivesMenu(ghMenu, ghDrvThread);

      return 1;
      }

      case WM_COMMAND:{

        //
        // The menu Identifiers for the drives are (potentially)
        // MM_DRIVE_NUM + 0 thru MM_DRIVE_NUM + 25. They all go to the
        // same case, so we will put the Menu ID in lParam, and
        // MM_DRIVE_NUM in LOWORD(wParam).
        //
        if( (LOWORD(wParam) - MM_DRIVE_NUM) <= 25 &&
            (LOWORD(wParam) - MM_DRIVE_NUM) >= 0 ){
            lParam = LOWORD(wParam);
            wParam = MM_DRIVE_NUM;
        }

        switch (LOWORD(wParam)) {
            //
            // If a drive is selected from the Drives menu, or clicked
            //  on the drives toolbar, the currently active child will
            //  switch to this drive. Message 'unconverted' (see top of
            //  WM_COMMAND case), and sent to DriveBarProc
            //
            case MM_DRIVE_NUM:{

                SendMessage(ghwndDrives, WM_COMMAND,
                            (WPARAM)lParam, (LPARAM)NULL);
                return 1;
            }

            //
            // Passes these WM_COMMAND messages to the appropriate active child
            //  window proc for processing
            //
            case MM_TAB:
            case MM_ESCAPE:
            case MM_ENCRYPT_DECRYPT:
case MM_SIGN:
case MM_VERIFY:
            case MM_EXPAND:{

                SendMessage(ghwndDrv, WM_COMMAND, wParam, lParam);
                return 1;
            }

            case MM_EXIT:{
                SendMessage(ghwndMain, WM_CLOSE, wParam, lParam);
                return 1;
            }

            //
            // Creates the drive enumeration thread to re-enumerate the
            //   available drives in the main menu.  Also sends a refresh
            //   to the active drive child, and repaints the window.
            //
            case MM_REFRESH: {
                DWORD   dwThreadID;

                //
                // Initialize/Refresh Drives linked list
                //

                if( WaitForSingleObject(ghDrvThread, 0) != WAIT_TIMEOUT ){

                    //
                    // Close previous Drive Thread handle before creating new handle.
                    //
                    CloseHandle( ghDrvThread );

                    ghDrvThread = CreateThread(NULL, 0,
                                           (LPTHREAD_START_ROUTINE)EnumDrives,
                                           (LPVOID)&glpDrives,
                                           0, &dwThreadID);

                    //
                    // Refresh active child, drive toolbar, and drives menu
                    //
                    SendMessage(ghwndDrv, WM_COMMAND, wParam, lParam);
                    SendMessage(ghwndDrives, WM_COMMAND, wParam, lParam);
                    UpdateDrivesMenu(ghMenu, ghDrvThread);

                    //
                    // Mark all for repaint
                    //
                    InvalidateRect(hwnd,NULL,TRUE);

                }
                else
                    MessageBeep(MB_ICONASTERISK);

                return 1;
            }

            //
            // Swaps the directory and file list boxes of the active drv child.
            //
            case MM_SWAP:{
                LPCINFO lpCInfo;
                RECT    rect;

                lpCInfo = (LPCINFO)GetWindowLong(ghwndDrv, GWL_USERDATA);

                if (!lpCInfo) {
                ErrorMsg(TEXT("MainWndProc: MM_SWAP: lpCInfo is NULL."));
                return FALSE;
                }

                //
                // Switch the flag which indicates which side the Directory
                //  LB is on.  This is used by the WM_SIZE case of DrvWndProc.
                //
                lpCInfo->fDirLeft = !lpCInfo->fDirLeft;

                //
                // Send size message with current size to active child,
                //   in order to redraw the listboxes.
                //
                if( !GetClientRect( ghwndDrv, &rect ) )
                    return 0;

                SendMessage( ghwndDrv, WM_SIZE, SIZENORMAL,
                             MAKELONG( rect.right - rect.left,
                                       rect.bottom - rect.top) );
                return 1;
            }

            case MM_KEEPCMD:{

                gfKeepCommandWin = !gfKeepCommandWin;

                if( gfKeepCommandWin )
                    CheckMenuItem( ghMenu, MM_KEEPCMD,
                                    MF_BYCOMMAND | MF_CHECKED);
                else
                    CheckMenuItem( ghMenu, MM_KEEPCMD,
                                MF_BYCOMMAND | MF_UNCHECKED);
            }
            break;

            //
            // Toggles the relative Drive Child orientaion between
            // Over/under and side/side.  gfDrvWndOrient is a flag checked
            // by WM_SIZE to size Drv children
            //
            case MM_ORIENT:{
                RECT    rect;

                if( gfDrvWndOrient == OVER_UNDER )
                    gfDrvWndOrient = SIDE_BY_SIDE;
                else
                    gfDrvWndOrient = OVER_UNDER;

                //
                // Send size message with current size to self (main window),
                //   in order to redraw the Drv children.
                //
                if( !GetClientRect( hwnd, &rect ) )
                    return 0;

                SendMessage( hwnd, WM_SIZE, SIZENORMAL,
                             MAKELONG( rect.right - rect.left,
                                       rect.bottom - rect.top) );

                InvalidateRect(ghwndDrv,NULL,TRUE);

                return 1;
            }

            //
            // Launches the About DialogBox.
            //
            case MM_ABOUT:{
                if (DialogBox(ghModule,
                  TEXT("AboutBox"),
                  ghwndMain,
                  (DLGPROC)AboutProc) == -1)

                ErrorMsg(TEXT("Main: About Dialog Creation Error!"));
                return 1;
            }

            case ID_HELP:
        if (!WinHelp(hwnd, TEXT("cf.hlp"), HELP_CONTENTS, 0L)) {
ErrorMsg(TEXT("MainWndProc: WinHelp failed."));
return 0;
}

        return 1;

case ID_ENCRYPTION_ALGORITHM:
bRet = DialogBox(ghModule,
     TEXT("ENCRYPTION_ALGORITHM"),
     ghwndMain,
     EncryptDlgProc);
if (bRet == TRUE)
InvalidateRect(ghwndMain, NULL, TRUE);
else if (bRet == FALSE)
return FALSE;
return 1;

case ID_SIGNATURE_ALGORITHM:
bRet = DialogBox(ghModule, TEXT("HASH_ALGORITHM"), ghwndMain, HashDlgProc);
if (bRet == TRUE)
InvalidateRect(ghwndMain, NULL, TRUE);
else if (bRet == FALSE)
return FALSE;
return 1;

case MM_PASS:
AlgidEncrypt = 0;
if (!GetPass(ghwndMain) || strlen(sPass) == 0) {
AlgidEncrypt = CALG_RC2;
return FALSE;
}
return 1;

case ID_HIDE: {
DWORD cm;

cm = CheckMenuItem(ghMenu, ID_HIDE, MF_CHECKED);

if (cm == MF_CHECKED) {
if (ShowSignatures())
CheckMenuItem(ghMenu, ID_HIDE, MF_UNCHECKED);
}
else if (cm == MF_UNCHECKED) {
if (HideSignatures())
CheckMenuItem(ghMenu, ID_HIDE, MF_CHECKED);
}
else {
ErrorMsg(TEXT("ID_HIDE: CheckMenuItem failed."));
return 0;
}

return 1;
}

default:
                return(DefWindowProc(hwnd, message, wParam, lParam));
        }
        return 1;
      }
      //
      // Whenever the window is resized, its children have to be
      //  resized accordingly.  The GetWindowLong values are the height
      //  of the windows queried by this function, and are set in the
      //  WM_CREATE cases of their respective WNDPROCs.
      //
      case WM_SIZE:{
        int DrvWndHeight;

        //
        // Always put the drives toolbar at the top of the frame window
        //
        MoveWindow(ghwndDrives,
           0,
           0,
           LOWORD(lParam),
           GetWindowLong(ghwndDrives, GWL_USERDATA),
           TRUE);

        //
        // Always size the Drive Children between the Drives and Command
        // windows. The width is set so that borders overlap.
        //

        if( gfDrvWndOrient == OVER_UNDER ){

            DrvWndHeight = ( HIWORD(lParam) -
                           GetWindowLong(ghwndDrives, GWL_USERDATA));

            MoveWindow(ghwndDrv,
                       -1,
                       GetWindowLong(ghwndDrives, GWL_USERDATA),
                       (LOWORD(lParam) + 2),
                       DrvWndHeight,
                       TRUE);
        }
        else{

            DrvWndHeight = HIWORD(lParam) -
                           GetWindowLong(ghwndDrives, GWL_USERDATA);

            MoveWindow(ghwndDrv,
                        -1,
                        GetWindowLong(ghwndDrives, GWL_USERDATA),
                        (LOWORD(lParam)/2 + 1)*2,
                        DrvWndHeight,
                        TRUE);
        }

        return 1;
      }

      case WM_DESTROY: {
        Logoff(hwnd);

   DeleteHashDirectory();

        free(lpDriveStrings);

DestroyMenu(ghMenu);

        SendMessage(ghwndDrv, WM_CLOSE, 0, 0);

        //
        // Close last drive thread handle,
        //  the created font, and the Drive list critical section.
        //
        CloseHandle( ghDrvThread );

        DeleteObject(ghFont);

        DeleteCriticalSection(&gDrvCS);

        PostQuitMessage(0);

    return 1;
      }

      default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
}

LRESULT WINAPI AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
      case WM_INITDIALOG:{
        return TRUE;
      }

      case WM_COMMAND:{
        if (wParam == IDOK)
            EndDialog(hDlg, wParam);
        break;
      }
    }

    return FALSE;

    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(hDlg);
}

/************************************************************************\
* DriveBarProc()
*
* Drive Toolbar procedure for displaying available drive Icons.
*  A bitmap button is displayed corresponding to the drive type of the
*  given drive, with the drive letter alongside.
*  ghwndDrives is the global handle assoc. w/ this window procedure.
\***********************************************************************/

LRESULT WINAPI DriveBarProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HBITMAP  hDrvBmp[NUM_BITMAPS];
    static HBRUSH   hBrush;         // background brush
    static int      nDrvEntryWidth; // width of button/letter entry
    static int      yVal;           // y value in toolbar for top left of bmp
    static LPBINFO  lpDrvButtonRoot;
    static int      nActiveDrvIndex;

    switch (message)
    {
        case WM_CREATE:{
            HDC        hDC;
            HGDIOBJ    hOldFont;
            TEXTMETRIC tm;
            LONG       lHeight;

            lpDrvButtonRoot = NULL;

            //
            // Load drive button bitmaps.
            //
            for(yVal = 0; yVal < NUM_BITMAPS; yVal++)
                hDrvBmp[yVal] = LoadBitmap( ghModule,
                                      MAKEINTRESOURCE(UB_BMP_MARKER + yVal) );

            //
            // Sets background color of Toolbar non-modal dialog children.
            //
            hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));

            hDC = GetDC(hwnd);

            hOldFont = SelectObject(hDC, ghFont);
            GetTextMetrics(hDC, &tm);

            //
            // base the height of the window on size of text
            //
            lHeight = tm.tmHeight + GetSystemMetrics(SM_CYBORDER) + 6;

            //
            // saved the window height, drive button entry width
            //   and button y starting value for later reference
            //
            SetWindowLong(hwnd, GWL_USERDATA, lHeight);

            //
            // Width of one button entry = spacing, button, sm. space,
            //   drive letter, spacing.
            //
            nDrvEntryWidth = DRIVE_BITMAP_SPACING + DRIVE_BITMAP_WIDTH +
                             DRIVE_LETTER_SPACING + tm.tmAveCharWidth +
                             DRIVE_BITMAP_SPACING;

            //
            // Center bitmaps (by height) in drive toolbar.
            //
            yVal = (lHeight - DRIVE_BITMAP_HEIGHT)/2;

            SelectObject(hDC, hOldFont);
            ReleaseDC(hwnd, hDC);

            SendMessage(hwnd, WM_COMMAND, (WPARAM)MM_REFRESH, (LPARAM)NULL);

            break;
        }

        case WM_COMMAND:{
            //
            // The button Identifiers for the drives are (potentially)
            // MM_DRIVE_NUM + 0 thru MM_DRIVE_NUM + 25. They all go to the
            // same case, so we will put the Menu ID in lParam, and
            // MM_DRIVE_NUM in LOWORD(wParam).
            //
            if( (LOWORD(wParam) - MM_DRIVE_NUM) <= 25 &&
                (LOWORD(wParam) - MM_DRIVE_NUM) >= 0 ){
                lParam = LOWORD(wParam);
                wParam = MM_DRIVE_NUM;
            }

            switch( LOWORD(wParam) ){
              case MM_REFRESH:{

                LPDINFO lpWalk;
                LPBINFO lpBWalk, lpBHold;
                LPCINFO lpCInfo;
                int     xVal = 0;
intnCount = MM_DRIVE_NUM;

                lpCInfo = (LPCINFO)GetWindowLong(ghwndDrv, GWL_USERDATA);

                if (!lpCInfo) {
                ErrorMsg(TEXT("DriveBarProc: MM_REFRESH: lpCInfo is NULL."));
                return FALSE;
                }

                //
                // Wait for Drive Thread to complete, if necessary.
                //
                WaitForSingleObject(ghDrvThread, INFINITE);
                EnterCriticalSection(&gDrvCS);

                //
                // Traverse DRVINFO linked list, creating drive buttons and
                //   allocating corresp. structures as necessary.
                //
                lpWalk = glpDrives;
                lpBWalk = lpDrvButtonRoot;

                while( lpWalk != NULL ){
                if( lpBWalk == NULL ){ //If at the end of the button list

                        // Allocate a LPBINFO (button) structure

                        lpBWalk = (LPBINFO)malloc((DWORD)sizeof(BINFO));

                        if (!lpBWalk) {
ErrorMsg(TEXT("DriveBarProc: MM_REFRESH: malloc failed."));
return 0;
}

                        lpBWalk->next = NULL;

                        // Create a button window
                        lpBWalk->hButton = (HANDLE)CreateWindow(TEXT("BUTTON"),
                                                 lpWalk->DriveName,
                                                 WS_CHILD | WS_VISIBLE |
                                                 BS_OWNERDRAW ,
                                                 xVal + DRIVE_BITMAP_SPACING,
                                                 yVal,
                                                 DRIVE_BITMAP_WIDTH,
                                                 DRIVE_BITMAP_HEIGHT,
                                                 hwnd,
                                                 (HMENU)nCount,
                                                 ghModule,
                                                 NULL);

                        // Insert structure into list
                        if( lpDrvButtonRoot == NULL)
                            lpDrvButtonRoot = lpBHold = lpBWalk;
                        else{
                            lpBHold->next = lpBWalk;
                            lpBWalk->next = NULL;
                        }

                    }

                    // An LPBINFO (button) structure exists: now initialize

                    // Set Title of Button (Drive Letter)

                    SetWindowText(lpBWalk->hButton, lpWalk->DriveName);

                    // Set Child Window ID for Button
                    // SetMenu(lpBWalk->hButton, (HMENU)nCount);

                    // Determine button up/down status
                    if( lpCInfo->lpDriveInfo == lpWalk ){
                        nActiveDrvIndex = nCount;
                        lpBWalk->fButtonDown = TRUE;
                    }
                    else
                        lpBWalk->fButtonDown = FALSE;

                    // Set a pointer to the corresponding drive in Drive list
                    lpBWalk->lpDrive = lpWalk;

                    nCount++;
                    xVal += nDrvEntryWidth;
                    lpBHold = lpBWalk;
                    lpBWalk = lpBWalk->next;

                    lpWalk = lpWalk->next;

                }

                LeaveCriticalSection(&gDrvCS);

                //
                // Free any remaining button windows.
                //
                while( lpBWalk != NULL ){
                    // NULL out new end of list
                    lpBHold->next = NULL;

                    // Assign pointer to doomed node
                    lpBHold = lpBWalk;
                    lpBWalk = lpBWalk->next;

                    // Free doomed node resources
                    if( !DestroyWindow(lpBHold->hButton) )
                        ErrorMsg(TEXT("DriveBarProc: Drive Button Destroy Error"));

                    free(lpBHold);
                }

                SendMessage(hwnd, WM_PAINT, (WPARAM)NULL, (LPARAM)NULL);

break;
              }

              //
              // switches the drive button to the newly active drv child's
              //   current drive.  Called by WM_MOUSEACTIVATE in DrvWndProc,
              //   as well as ChangeDrive.
              //   lParam contains the drive linked list pointer of the active
              //   drv child's LPCINFO struct.
              //
              case MM_ACTIVEDRV:{
                LPBINFO lpBWalk = lpDrvButtonRoot;
                int     nCount = 0;

                //
                // 'unpush' old active button
                //
                for( nCount = MM_DRIVE_NUM; nCount < nActiveDrvIndex; nCount++)
                    lpBWalk = lpBWalk->next;
                lpBWalk->fButtonDown = FALSE;

                InvalidateRect(lpBWalk->hButton, NULL, FALSE);

                //
                // change active drive to new before redrawing old.
                //  'push' new active button
                //
                lpBWalk = lpDrvButtonRoot;
                nCount = MM_DRIVE_NUM;
                while( lpBWalk->lpDrive != (LPDINFO)lParam){
                    lpBWalk = lpBWalk->next;
                    nCount++;
                }

                nActiveDrvIndex = nCount;

                lpBWalk->fButtonDown = TRUE;

                InvalidateRect(lpBWalk->hButton, NULL, FALSE);

                break;
              }

              //
              // Changes drive of active child.  ButtonID in lParam.
              //
              case MM_DRIVE_NUM:{

                LPBINFO lpBWalk = lpDrvButtonRoot;

int     nCount = 0;
                TCHAR    szDrvBuff[DIRECTORY_STRING_SIZE];

                //
                // if drive chosen is already current drive, leave.
                //
                if( nActiveDrvIndex == (int)lParam )
                    break;

                //
                // unpush' old active button
                //
                for( nCount = MM_DRIVE_NUM; nCount < nActiveDrvIndex; nCount++)
                    lpBWalk = lpBWalk->next;
                lpBWalk->fButtonDown = FALSE;

                //
                // change active drive to new before redrawing old.
                //
                nActiveDrvIndex = (int)lParam;

                InvalidateRect(lpBWalk->hButton, NULL, FALSE);

                //
                // 'push' new active button
                //
                lpBWalk = lpDrvButtonRoot;

                for( nCount = MM_DRIVE_NUM; nCount < nActiveDrvIndex; nCount++)
                    lpBWalk = lpBWalk->next;
                lpBWalk->fButtonDown = TRUE;

                InvalidateRect(lpBWalk->hButton, NULL, FALSE);

                GetWindowText(lpBWalk->hButton, szDrvBuff,
                              DIRECTORY_STRING_SIZE);

                if( !ChangeDrive(szDrvBuff, (DWORD)nActiveDrvIndex) ){
                    ErrorMsg(TEXT("Error changing Drives.\r\n"));
                    return 0;
                }

                break;
              }
            }
            return 1;
        }

        //
        // Sent by all created buttons for initialization purposes.
        //
        case WM_MEASUREITEM:{
            LPMEASUREITEMSTRUCT lpMIS;

            lpMIS = (LPMEASUREITEMSTRUCT)lParam;

            lpMIS->CtlType = ODT_BUTTON;
            lpMIS->CtlID = (UINT)wParam;
            lpMIS->itemWidth = DRIVE_BITMAP_WIDTH;
            lpMIS->itemHeight = DRIVE_BITMAP_HEIGHT;

            return 1;
        }

        //
        // Sent by owner draw drive buttons when needing redrawing.
        //
        case WM_DRAWITEM:{
            LPBINFO lpBWalk = lpDrvButtonRoot;
            int     nCount;
            int     nBmpIndex;
            HDC     hDC;
            HDC     hCompatDC;
            HGDIOBJ hOldBitmap;
            TCHAR    szDrvBuff[DIRECTORY_STRING_SIZE];
            LPDRAWITEMSTRUCT    lpDIS;

            lpDIS = (LPDRAWITEMSTRUCT)lParam;

            for( nCount = MM_DRIVE_NUM; nCount < (int)wParam; nCount++)
                lpBWalk = lpBWalk->next;

            //
            // If not the current selected button, handle button stuff.
            //
            if( (int)wParam != nActiveDrvIndex ){
                //
                // mousebutton is down...
                //
                if( lpDIS->itemAction & ODA_SELECT ){
                    //
                    // left button region, 'unpush' button
                    //
                    if( lpDIS->itemState == (UINT)ODS_FOCUS )
                        lpBWalk->fButtonDown = FALSE;
                    //
                    // clicked on a button, draw 'pushed' button
                    //
                    if( lpDIS->itemState == (UINT)(ODS_SELECTED | ODS_FOCUS))
                        lpBWalk->fButtonDown = TRUE;
                }
            }

            //
            // draw current state of button.
            //
            GetWindowText(lpDIS->hwndItem, szDrvBuff,
                          DIRECTORY_STRING_SIZE);

            szDrvBuff[1] = TEXT('\0');

            hCompatDC = CreateCompatibleDC(lpDIS->hDC);
            hOldBitmap = CreateCompatibleBitmap(hCompatDC,
                                                DRIVE_BITMAP_WIDTH,
                                                DRIVE_BITMAP_HEIGHT);

            nBmpIndex = GetDriveBitmap(lpBWalk);

            SelectObject( hCompatDC, hDrvBmp[nBmpIndex] );

            if( !hOldBitmap )
                ErrorMsg(TEXT("WM_DRAWITEM: SelectObject failure."));

            if( !BitBlt(lpDIS->hDC, lpDIS->rcItem.left, lpDIS->rcItem.top,
                   DRIVE_BITMAP_WIDTH,
                   DRIVE_BITMAP_HEIGHT,
                   hCompatDC, 0, 0, SRCCOPY) )
                ErrorMsg(TEXT("WM_DRAWITEM: BitBlt failure."));

            SelectObject( hCompatDC, hOldBitmap);
            DeleteDC(hCompatDC);

            hDC = GetDC(hwnd);
            SetBkMode(hDC, OPAQUE);
            SetTextColor(hDC, GetSysColor(COLOR_MENUTEXT) );
            SetBkColor(hDC, GetSysColor(COLOR_MENU) );

            TextOut(hDC,
                    ((int)(wParam - MM_DRIVE_NUM) * nDrvEntryWidth) +
                        DRIVE_BITMAP_SPACING + DRIVE_BITMAP_WIDTH +
                        DRIVE_LETTER_SPACING,
                    (GetSystemMetrics(SM_CYBORDER) + 6)/2,
                    szDrvBuff, 1);

            SetBkMode(hDC, OPAQUE);

            ReleaseDC(hwnd, hDC);

DeleteObject(hOldBitmap);

            break;
        }

        case WM_PAINT:{
            HDC     hCompatDC;
            RECT     rc;
            PAINTSTRUCT ps;

            //
            // Paint btnshadow background.
            //
            GetClientRect(hwnd, &rc);

            BeginPaint(hwnd, &ps);

            hCompatDC = CreateCompatibleDC(ps.hdc);
            FillRect(ps.hdc, &rc, hBrush);

            EndPaint(hwnd, &ps);

            DeleteDC(hCompatDC);

            return(TRUE);
        }

        case WM_DESTROY:{
            LPBINFO p, q;
            LPDINFO p1, q1;

            DeleteObject(hBrush);

            for (p = lpDrvButtonRoot; p; p = q) {
q = p->next;
free(p);
}

            for (p1 = glpDrives; p1; p1 = q1) {
            q1 = p1->next;
            free(p1);
            p1 = NULL;
            }

            for(yVal = 0; yVal < NUM_BITMAPS; yVal++)
                DeleteObject(hDrvBmp[yVal]);

            break;
        }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

/************************************************************************\
* GetDriveBitmap()
*
* Determines the appropriate index into the drive button bitmap array
* (hDrvBmp[]), given a pointer to a drive info structure (LPDINFO)
*
* lpWalk          -   pointer to LPDINFO structure.
* lpCurrentDrv    -   pointer to current drive of active child.
\***********************************************************************/

int GetDriveBitmap(LPBINFO lpBWalk)
{
    int nBmpIndex;

    EnterCriticalSection(&gDrvCS);

    switch( lpBWalk->lpDrive->DriveType ){
        case DRIVE_REMOVABLE:{
          nBmpIndex = UB_FLOPPY1 - UB_BMP_MARKER;
          break;
        }

        case DRIVE_REMOTE:{
          nBmpIndex = UB_REMOTE1 - UB_BMP_MARKER;
            break;
        }

        case DRIVE_CDROM:{
          nBmpIndex = UB_CD1 - UB_BMP_MARKER;
            break;
        }

        case DRIVE_FIXED:
        default:{
          nBmpIndex = UB_FIXED1 - UB_BMP_MARKER;
            break;
        }
    }

    LeaveCriticalSection(&gDrvCS);

    if( lpBWalk->fButtonDown == TRUE )
        nBmpIndex++;

    return(nBmpIndex);
}

/********************************************************************************\
* ChangeDrive()
*
*   Changes the current drive of the child.  Called by the MM_DRIVE_NUM
*   cases in MainWndProc and DriveBarProc.  This is caused by choosing a
*   Drive menu item or selecting a drive button from the drive toolbar.
*
*   lpszDriveName -   points to a buffer containing the name of the drive
*   DriveID       -   points to the ID of the Menu item or button, which
*                         corresponds to the index into the drives linked list
*                         of the new drive.
\********************************************************************************/

BOOL ChangeDrive(LPTSTR lpszDriveName, DWORD DriveIndex)
{
    LPCINFO     lpCInfo;
    LPDINFO     lpWalk;
    DWORD       dwLoop;
    UINT        nDriveType;

    //
    // Retrieve active child handle.
    //
    if( (ghwndDrv != ghwndDrv) ){
        ErrorMsg(TEXT("A Drive Window Must be Active."));
        return 0;
    }

    //
    // Retrieving the child window's DRVCHILDINFO data
    //
    lpCInfo = (LPCINFO)GetWindowLong(ghwndDrv, GWL_USERDATA);

    if (!lpCInfo) {
    ErrorMsg(TEXT("ChangeDrive: lpCInfo is NULL."));
    return FALSE;
    }

    //
    // Enter Drive list critical section
    //
    EnterCriticalSection(&gDrvCS);

    //
    // if removable drive, check for existing media.
    //
    nDriveType = GetDriveType(lpszDriveName);
    if( nDriveType == DRIVE_REMOVABLE ||
        nDriveType == DRIVE_CDROM ){
        dwLoop = (DWORD)IDOK;

        while( !CheckRM(lpszDriveName) && (dwLoop == (DWORD)IDOK) ){

           dwLoop = (DWORD)MessageBox(ghwndMain,
                           TEXT("CRYPTFILER: Insert some media in drive"),
                           lpszDriveName, MB_OKCANCEL);
        }

        if( dwLoop == (DWORD)IDCANCEL ){
            SendMessage(ghwndDrives, WM_COMMAND, MM_ACTIVEDRV,
                        (LPARAM)lpCInfo->lpDriveInfo);
        LeaveCriticalSection(&gDrvCS);
            return 0;
        }
    }

    //
    // set lpDriveInfo member to associated drive struct.
    //
    lpWalk = glpDrives;
    for( dwLoop = 0; dwLoop < DriveIndex - MM_DRIVE_NUM;
         dwLoop++)
         lpWalk = lpWalk->next;

    lpCInfo->lpDriveInfo = lpWalk;

    lstrcpy(lpCInfo->CaptionBarText, lpWalk->DriveName);

    LeaveCriticalSection(&gDrvCS);

    //
    // This will terminate any currently running drive thread.
    //
    SendMessage(ghwndDrv, WM_COMMAND, MM_ESCAPE, (LPARAM)0);
    lpCInfo->fEscape = FALSE;

    //
    // enact the drive change.
    //
    PostMessage(ghwndDrv, WM_COMMAND, MM_REFRESH, (LPARAM)0);

    return 1;
}

/********************************************************************************\
* UpdateDrivesMenu()
*
* Adds current drives from the glpDrives linked list to the TEXT('Drives') menu
*
* Input: hDrivesMenu - handle to TEXT('Drives') Menu
*        hThread    - used to wait for drives thread to terminate
\********************************************************************************/

BOOL UpdateDrivesMenu(HMENU hMenu, HANDLE hThread)
{
    HMENU   hDrivesMenu;
    int     NumMenuItems;
    DWORD   dwLoop;
    LPDINFO lpWalk;

    //
    // Remove list of drive menu items from Drive menu, if any.
    //
    hDrivesMenu = GetSubMenu( hMenu, DRIVE_MENU_NUM);
    if(!hDrivesMenu){
        ErrorMsg(TEXT("UpdateDrivesMenu: GetSubMenu error."));
        return FALSE;
    }

    if((NumMenuItems = GetMenuItemCount(hDrivesMenu)) == -1) {
        ErrorMsg(TEXT("Main Refresh: Menu Item Count Error."));
return FALSE;
}

    //
    // Delete previous menu items.
    //
    for( dwLoop = 0; dwLoop < (DWORD)NumMenuItems; dwLoop++)
        if(!DeleteMenu(hDrivesMenu, 0, MF_BYPOSITION)){
            ErrorMsg(TEXT("Main Refresh: Menu Item Delete Error."));
            return FALSE;
        }

    //
    // Wait for Enumdrv Thread to terminate, and
    //   enter drive list critical section
    //
    if (WaitForSingleObject(hThread, INFINITE) == WAIT_FAILED) {
ErrorMsg(TEXT("UpdateDrivesMenu: WaitForSingleObject failed."));
return FALSE;
}

    EnterCriticalSection(&gDrvCS);

    //
    // Fill drive menu from glpDrives linked list
    //
    NumMenuItems = 0;
    lpWalk = glpDrives;

    while(lpWalk != NULL){
        if(!InsertMenu(hDrivesMenu, NumMenuItems, MF_STRING | MF_BYPOSITION
        | MF_ENABLED, MM_DRIVE_NUM + NumMenuItems, lpWalk->DriveName)) {
            ErrorMsg(TEXT("Main Refresh: Menu Item Insert Error."));
return FALSE;
}
        NumMenuItems++;
        lpWalk = lpWalk->next;
    }

    LeaveCriticalSection(&gDrvCS);

    return TRUE;
}

/***************************************************************\
* ErrorMsg()
*
*   Displays a Message Box with a given error message.
*
\***************************************************************/

void ErrorMsg(LPTSTR szMsg)
{
    TCHAR szHold[DIRECTORY_STRING_SIZE + 1];

    lstrcpy( szHold, szMsg );
    lstrcat( szHold, TEXT("\n") );

   OutputDebugString(szHold);
}


CFILER.H

// CFILER.H

#ifndefCRYPTFILER_INCLUDED
#define CRYPTFILER_INCLUDED

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <cderr.h>
#include <ctype.h>
#include <stdarg.h>
#include <limits.h>
#include <wincrypt.h>
#include "GLOBALS.H"
#include "ENUMDRV.H"
#include "DRVPROC.H"
#include "EXPDIR.H"
#include "CFILER.H"
#include "RESOURCE.H"
#include "CRP.H"
#include "TABLE.H"
#include "FILLFILE.H"

#ifdef UNICODE
#include <TCHAR.H>
#else
#include <STRING.H>
#endif

BOOL    InitializeApp           (VOID);
VOID    ErrorMsg                (LPTSTR);
LRESULT WINAPI MainWndProc      (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI AboutProc        (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI DriveBarProc     (HWND, UINT, WPARAM, LPARAM);
INTGetDriveBitmap(LPBINFO);
BOOL ChangeDrive(LPTSTR, DWORD);
BOOL    UpdateDrivesMenu        (HMENU, HANDLE);

#endif // #ifndef CRYPTFILER_INCLUDED


CRP.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// CRP.C
#include "cfiler.h"

HCRYPTPROVhMe;
ALG_IDAlgidEncrypt;
ALG_IDAlgidSign;
TCHARsPass[BUF_SIZE];
TCHARszPassName[BUF_SIZE];

extern HANDLE ghModule;
extern HMENUghMenu;

/************************************************************************\
* EnterPass()
*
* Dialog procedure for the "PASSWORD" dialog box.
*
* Stores the password entered by the user into
* the global buffer sPass
\************************************************************************/

WINAPI EnterPass(HWND hDlg, UINT wMsgID, UINT wParam, LPARAM lParam)  {
TCHAR szMessage[BUF_SIZE];

switch(wMsgID) {
case WM_INITDIALOG:
lstrcpy(szMessage, TEXT("Enter password: "));
lstrcat(szMessage, szPassName);
SetDlgItemText(hDlg, IDC_PASSTEXT, szMessage);
SetDlgItemText(hDlg, IDC_EDIT1, TEXT("\0"));
SetDlgItemText(hDlg, IDC_EDIT2, TEXT("\0"));

return FALSE;

case WM_COMMAND:
switch(LOWORD(wParam)) {
TCHAR szVerify[BUF_SIZE];

case IDOK:
if (!GetDlgItemText(hDlg, IDC_EDIT1, sPass, BUF_SIZE)) {
                if (MessageBox(hDlg, TEXT("No password entered."), NULL, MB_OK) == IDOK) {
EndDialog(hDlg, FALSE);
return FALSE;
}
}

SetFocus(GetDlgItem(hDlg, IDC_EDIT2));

if (!GetDlgItemText(hDlg, IDC_EDIT2, szVerify, BUF_SIZE)) {
MessageBox(hDlg, TEXT("No password entered."), NULL, MB_OK);
EndDialog(hDlg, FALSE);
return FALSE;
}

if (lstrcmp(sPass, szVerify)) {
MessageBox(hDlg, TEXT("Could not verify password."), NULL, MB_OK);
EndDialog(hDlg, FALSE);
return FALSE;
}

EndDialog(hDlg, TRUE);

return TRUE;

case IDCANCEL:
EndDialog(hDlg, -1);
return -1;

default:
return FALSE;
}
break;
}
return FALSE;
}

/************************************************************************\
* CryptDlgProc()
*
* Dialog procedure for all dialog boxes of the
* application besides "PASSWORD", "ENCRYPTION_ALGORITHM,"
* and "HASH_ALGORITHM."
\************************************************************************/

LRESULT WINAPI CryptDlgProc(HWND hDlg, UINT wMsgID, UINT wParam, LPARAM lParam) {
switch(wMsgID) {
case WM_INITDIALOG:
return FALSE;

case WM_COMMAND:
switch(LOWORD(wParam)) {
case IDOK:
EndDialog(hDlg, TRUE);
return TRUE;

case IDCANCEL:
EndDialog(hDlg, FALSE);
return TRUE;

default:
return FALSE;
}
break;
}
return FALSE;
}

/************************************************************************\
* EncryptDlgProc()
*
* Dialog procedure for "ENCRYPTION_ALGORITHM" dialog box.
\************************************************************************/

LRESULT WINAPI EncryptDlgProc(HWND hDlg, UINT wMsgID, UINT wParam, LPARAM lParam) {
switch(wMsgID) {
case WM_INITDIALOG:
if (AlgidEncrypt == CALG_RC4)
CheckRadioButton(hDlg, IDC_RC2, IDC_RC4, IDC_RC4);
else
CheckRadioButton(hDlg, IDC_RC2, IDC_RC4, IDC_RC2);
return FALSE;

case WM_COMMAND:
switch(LOWORD(wParam)) {
case IDC_RC2:
AlgidEncrypt = CALG_RC2;
return TRUE;

case IDC_RC4:
AlgidEncrypt = CALG_RC4;
return TRUE;

case IDOK:
if (!AlgidEncrypt)
AlgidEncrypt = CALG_RC2;
EndDialog(hDlg, TRUE);
return TRUE;

case IDCANCEL:
EndDialog(hDlg, FALSE);
return TRUE;

default:
return FALSE;
}
break;
}
return FALSE;
}

/************************************************************************\
* HashDlgProc()
*
* Dialog procedure for "HASH_ALGORITHM" dialog box
\************************************************************************/

LRESULT WINAPI HashDlgProc(HWND hDlg, UINT wMsgID, UINT wParam, LPARAM lParam) {
switch(wMsgID) {
case WM_INITDIALOG:
if (AlgidSign == CALG_SHA)
CheckRadioButton(hDlg, IDC_MD4, IDC_SHA, IDC_SHA);
else if (AlgidSign == CALG_MD5)
CheckRadioButton(hDlg, IDC_MD4, IDC_SHA, IDC_MD5);
else
CheckRadioButton(hDlg, IDC_MD4, IDC_SHA, IDC_MD4);
return FALSE;

case WM_COMMAND:
switch(LOWORD(wParam)) {
case IDC_MD4:
AlgidSign = CALG_MD4;
return TRUE;

case IDC_MD5:
AlgidSign = CALG_MD5;
return TRUE;

case IDC_SHA:
AlgidSign = CALG_SHA;
return TRUE;

case IDOK:
EndDialog(hDlg, TRUE);
return TRUE;

case IDCANCEL:
EndDialog(hDlg, FALSE);
return TRUE;

default:
return FALSE;
}
break;
}
return FALSE;
}

/************************************************************************\
* GetPass()
*
* input:
* hWnd - HWND of caller
*
* purpose:
* Creates a dialog box prompting the user to enter a password.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL GetPass(HWND hWnd) {
INT bRet = 0;

do {
if (bRet == -1)
break;

// Prompt the user to enter a password.

bRet = DialogBox(ghModule, TEXT("PASSWORD"), hWnd, EnterPass);
} while (!bRet || !lstrcmp(sPass, TEXT("\0")) || !lstrcmp(sPass, TEXT("")));

AlgidEncrypt = 0;

return TRUE;
}

/************************************************************************\
* GetfnKey()
*
* purpose:
* Retrieves fnKey from the registry.
* fnKey is used to encrypt and decrypt filenames.
* If fnKey does not exist in the registry, GetfnKey creates it.
*
* returns
* a valid key if successful
* 0 if unsuccessful.
\************************************************************************/

HCRYPTKEY GetfnKey(HWND hWnd) {
BYTE pbFileKeyBlob[BUF_SIZE];
DWORD dwFileBlobLen = BUF_SIZE;
LONGlRv;
HKEYhKey;
DWORDdwType;
DWORDdwSize;
DWORDdwDisposition;
HCRYPTKEYphEncryptPubKey;
HCRYPTKEYhfnKey;

if (!GetEncryptPublicKey(hWnd, &phEncryptPubKey)) {
ErrorMsg(TEXT("GetfnKey: GetEncryptPublicKey failed."));
return (HCRYPTKEY)0;
}

#ifdef WIN95

lRv = RegOpenKeyEx(HKEY_CURRENT_USER,
    TEXT("SOFTWARE\\Microsoft\\Cryptfiler.ini"),
   0,
   KEY_ALL_ACCESS,
   &hKey);

#else

lRv = RegOpenKeyEx(HKEY_CURRENT_USER,
    TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\IniFileMapping\\Cryptfiler.ini"),
   0,
   KEY_ALL_ACCESS,
   &hKey);
#endif

if (lRv != ERROR_SUCCESS) {

// not in registry; we must create.

#ifdef WIN95

lRv = RegCreateKeyEx(HKEY_CURRENT_USER,
  TEXT("SOFTWARE\\Microsoft\\Cryptfiler.ini"),
 0,
 NULL,
 REG_OPTION_NON_VOLATILE,
 KEY_ALL_ACCESS,
 NULL,
 &hKey,
 &dwDisposition);
#else
lRv = RegCreateKeyEx(HKEY_CURRENT_USER,
  TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\IniFileMapping\\Cryptfiler.ini"),
 0,
 NULL,
 REG_OPTION_NON_VOLATILE,
 KEY_ALL_ACCESS,
 NULL,
 &hKey,
 &dwDisposition);
#endif

if (lRv != ERROR_SUCCESS) {
ErrorMsg(TEXT("GetfnKey: RegCreateKeyEx failed."));
return (HCRYPTKEY)0;
}

if (RCRYPT_FAILED(CryptGenKey(hMe, CALG_RC4, CRYPT_EXPORTABLE, &hfnKey))) {
ErrorMsg(TEXT("GetfnKey: CryptGenKey failed."));
return (HCRYPTKEY)0;
}

//Export the key so that it can be stored in the registry.

if (RCRYPT_FAILED(CryptExportKey(hfnKey,
  phEncryptPubKey,
  SIMPLEBLOB,
  0,
  pbFileKeyBlob,
  &dwFileBlobLen))) {
ErrorMsg(TEXT("GetfnKey: CryptExportKey failed."));
return (HCRYPTKEY)0;
}

// Store the key blob in the registry.

if (RegSetValueEx(hKey,
  TEXT("fnKey"),
  0,
  REG_BINARY,
  pbFileKeyBlob,
  dwFileBlobLen * sizeof(*pbFileKeyBlob)) != ERROR_SUCCESS) {

ErrorMsg(TEXT("GetfnKey: RegSetValueEx failed."));
return (HCRYPTKEY)0;
}
}
else {
// in registry. We must extract.

dwType = 0;
dwFileBlobLen = 0;
dwSize = BUF_SIZE;

if (RegQueryValueEx(hKey,
TEXT("fnKey"),
NULL,
&dwType,
pbFileKeyBlob,
&dwSize) != ERROR_SUCCESS)
{
MessageBox(hWnd, TEXT("GetfnKey: RegQueryValueEx failed querrying pbFileKeyBlob."), NULL, MB_OK);
return (HCRYPTKEY)0;
};

// Read the key blob from the disk into a Buf.

if (!dwSize || (dwSize > BUF_SIZE)) {
ErrorMsg(TEXT("GetfnKey: dwSize is not in acceptable range."));
return FALSE;
}

// Import the key whose blob is contained in the Buf pbDecryptBlob

dwFileBlobLen = dwSize;

if (RCRYPT_FAILED(CryptImportKey(hMe,
  pbFileKeyBlob,
  dwFileBlobLen,
  0,
  0,
  &hfnKey))) {

MessageBox(hWnd, TEXT("GetfnKey: CryptImportKey failed"), NULL, MB_OK);
return (HCRYPTKEY)0;
}
}

return hfnKey;
}

/************************************************************************\
* Logon()
*
* input:
* hWnd - HWND of caller
*
* purpose:
* Creates a dialog box which says "Performing context acquisition."
* Calls CryptAcquireContext.
* Destroys the dialog box
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL Logon(HWND hWnd) {
HWNDhwndDlg;
TCHARpszName[64];

pszName[0] = TEXT('\0');
hwndDlg = CreateDialog(ghModule, TEXT("ACQUIRE_CONTEXT"), hWnd, CryptDlgProc);

if (!hwndDlg) {
ErrorMsg(TEXT("Logon: CreateDialog failed."));
return FALSE;
}

if (RCRYPT_FAILED(CryptAcquireContext(&hMe, pszName, MS_DEF_PROV, PROV_RSA_FULL, 0))) {
if (RCRYPT_FAILED(CryptAcquireContext(&hMe,
  pszName,
  TEXT(MS_DEF_PROV),
  PROV_RSA_FULL,
  CRYPT_NEWKEYSET))) {
EndDialog(hwndDlg, FALSE);
MessageBox(hWnd, TEXT("Logon: CryptAcquireContext failed."), NULL, MB_OK);
return FALSE;
}
}

EndDialog(hwndDlg, FALSE);
return TRUE;

return FALSE;
}

/************************************************************************\
* Logoff()
*
* input:
* hWnd - HWND of caller
*
* purpose:
* Calls CryptReleaseContext.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL Logoff(HWND hWnd)
{
if (hMe) {
if (RCRYPT_FAILED(CryptReleaseContext(hMe, 0))) {
        ErrorMsg(TEXT("Logoff: CryptReleaseContext failed."));
        return FALSE;
    }
}
else {
ErrorMsg(TEXT("Logoff: hMe is NULL."));
return FALSE;
}

return TRUE;
}

/************************************************************************\
* GetEncryptPublicKey()
*
* input:
* hWnd - HWND specified by caller
* phEncryptPubKey - address to store key
*
* purpose: calls CryptGetUserKey to obtain the AT_KEYEXCHANGE public
* key.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL GetEncryptPublicKey(HWND hWnd, HCRYPTKEY *phEncryptPubKey) {
if (!phEncryptPubKey) {
ErrorMsg(TEXT("GetEncryptPubKey: phEncryptPubKey is NULL."));
return FALSE;
}

if (RCRYPT_FAILED(CryptGetUserKey(hMe, AT_KEYEXCHANGE, phEncryptPubKey))) {
if (RCRYPT_FAILED(CryptGenKey(hMe, AT_KEYEXCHANGE, 0, phEncryptPubKey))) {
ErrorMsg(TEXT("GetEncryptPublicKey: CryptGenKey failed."));
return TRUE;
}
}

return TRUE;
}

/************************************************************************\
* GetSignaturePublicKey()
*
* input:
* hWnd - HWND specified by caller
* phSignaturePubKey - address to store key
*
* purpose: calls CryptGetUserKey to obtain the AT_SIGNATURE public
* key.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL GetSignaturePublicKey(HWND hWnd, HCRYPTKEY *phSignaturePubKey) {
if (!phSignaturePubKey) {
ErrorMsg(TEXT("GetSignaturePublicKey: phSignaturePubKey is NULL."));
return FALSE;
}

if (RCRYPT_FAILED(CryptGetUserKey(hMe, AT_SIGNATURE, phSignaturePubKey))) {
if (RCRYPT_FAILED(CryptGenKey(hMe, AT_SIGNATURE, 0, phSignaturePubKey))) {
ErrorMsg(TEXT("GetSignaturePublicKey: CryptGenKey failed."));
return FALSE;
}
}

return TRUE;
}

/************************************************************************\
* MakeHashDirectory()
*
* purpose:
* Creates the directory \sig on the same drive as the
* system directory as a hidden directory if
* it doesn't exist and checks the menu item ID_HIDE.
* If it does exist and it is hidden it checks the menu item ID_HIDE.
* If it does exist and it is not hidden it sets the menu item ID_HIDE
* to be unchecked.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL MakeHashDirectory(VOID) {
DWORD dwFileAttributes;
TCHAR szSigDir[PATH_SIZE];

GetSigDir(szSigDir);

// Does this directory exist?

if (GetFileAttributes(szSigDir) == 0xFFFFFFFF) {
// Create the directory
if (!CreateDirectory(szSigDir, NULL)) {
ErrorMsg(TEXT("MakeHashDirectory: CreateDirectory failed."));
return FALSE;
}
// Make the directory hidden
if (!SetFileAttributes(szSigDir, FILE_ATTRIBUTE_HIDDEN)) {
ErrorMsg(TEXT("Another process is using the \\sig directory."));
return FALSE;
}
}

dwFileAttributes = GetFileAttributes(szSigDir);

// Toggle the "Hide Signatures" menu item appropriately.

if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
      CheckMenuItem(ghMenu, ID_HIDE, MF_CHECKED);
    else
    CheckMenuItem(ghMenu, ID_HIDE, MF_UNCHECKED);

    return TRUE;
}

/************************************************************************\
* DeleteHashDirectory()
*
* purpose:
* Deletes the directory \sig on the same drive as the system directory
* if the \sig directory is empty. This function is called when the
* application terminates.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL DeleteHashDirectory(VOID) {
TCHAR szSigDir[PATH_SIZE];
TCHAR szFiles[PATH_SIZE];
WIN32_FIND_DATA FindFileData;
HANDLE hFindFile;

GetSigDir(szSigDir);

lstrcpy(szFiles, szSigDir);
lstrcat(szFiles, TEXT("\\*"));

// Does this directory exist?

if (GetFileAttributes(szSigDir) == 0xFFFFFFFF) {
return TRUE;
}

hFindFile = FindFirstFile(szFiles, &FindFileData);

FindNextFile(hFindFile, &FindFileData);

if (!FindNextFile(hFindFile, &FindFileData)) {
FindClose(hFindFile);
SetFileAttributes(szSigDir, FILE_ATTRIBUTE_NORMAL);
if (!RemoveDirectory(szSigDir)) {
ErrorMsg(TEXT("DeleteHashDirectory: RemoveDirectory failed."));
return FALSE;
}
}
else
    FindClose(hFindFile);

    return TRUE;
}

/************************************************************************\
* HideSignatures()
*
* purpose:
* Hides the directory \sig and its contents
*
* returns
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL HideSignatures(VOID) {
WIN32_FIND_DATA FindFileData;
HANDLE hFindFile;
TCHAR szFiles[PATH_SIZE];
TCHAR szFileName[PATH_SIZE];
TCHAR szSigDir[PATH_SIZE];

GetSigDir(szSigDir);
lstrcpy(szFiles, szSigDir);
lstrcat(szFiles, TEXT("\\*"));

// hide the directory.

if (!SetFileAttributes(szSigDir, FILE_ATTRIBUTE_HIDDEN)) {
ErrorMsg(TEXT("Another process is using the \\sig directory."));
return FALSE;
}

hFindFile = FindFirstFile(szFiles, &FindFileData);

if (hFindFile == INVALID_HANDLE_VALUE) {
SetFileAttributes(szSigDir, FILE_ATTRIBUTE_NORMAL);
return FALSE;
}

// Hide all the signature files.

do {
lstrcpy(szFileName, szSigDir);
lstrcat(szFileName, TEXT("\\"));
lstrcat(szFileName, FindFileData.cFileName);

if (!CFilerIsDirectory(szFileName)) {
if (!SetFileAttributes(szFileName, FILE_ATTRIBUTE_HIDDEN)) {
SetFileAttributes(szSigDir, FILE_ATTRIBUTE_NORMAL);
FindClose(hFindFile);
ErrorMsg(TEXT("Another process is using a signature file."));
return FALSE;
}
}
} while (FindNextFile(hFindFile, &FindFileData)
|| GetLastError() != ERROR_NO_MORE_FILES);

FindClose(hFindFile);

return TRUE;
}

/************************************************************************\
* ShowSignatures()
*
* purpose:
* "unhides" the directory \sig and its contents
*
* returns
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL ShowSignatures(VOID) {
WIN32_FIND_DATA FindFileData;
HANDLE hFindFile;
TCHAR szFiles[PATH_SIZE];
TCHAR szFileName[PATH_SIZE];
TCHAR szSigDir[PATH_SIZE];

GetSigDir(szSigDir);
lstrcpy(szFiles, szSigDir);
lstrcat(szFiles, TEXT("\\*"));

// unhide the directory

if (!SetFileAttributes(szSigDir, FILE_ATTRIBUTE_NORMAL)) {
ErrorMsg(TEXT("Another process is using the \\sig directory."));
return FALSE;
}

hFindFile = FindFirstFile(szFiles, &FindFileData);

if (hFindFile == INVALID_HANDLE_VALUE) {
SetFileAttributes(szSigDir, FILE_ATTRIBUTE_HIDDEN);
return FALSE;
}

// unhide all the signature files.

do {
lstrcpy(szFileName, szSigDir);
lstrcat(szFileName, TEXT("\\"));
lstrcat(szFileName, FindFileData.cFileName);

if (!CFilerIsDirectory(szFileName)) {
if (!SetFileAttributes(szFileName, FILE_ATTRIBUTE_NORMAL)) {
SetFileAttributes(szSigDir, FILE_ATTRIBUTE_HIDDEN);
FindClose(hFindFile);
ErrorMsg(TEXT("Another process is using a signature file."));
return FALSE;
}
}
} while (FindNextFile(hFindFile, &FindFileData)
|| GetLastError() != ERROR_NO_MORE_FILES);

FindClose(hFindFile);

return TRUE;
}

/************************************************************************\
* c2r64()
*
* input:
* i - integer value to be converted to a radix64 character
* c - address where character is to be stored
*
* purpose:
* converts an integer between 0 and 63 to a character.
* 0-25 -> A-Z
* 26 - 51 -> a-z
* 52 - 61 -> 0-9
* 62 -> +
* 63 -> _
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL c2r64(INT i, LPTSTR c) {
if (!c) {
ErrorMsg(TEXT("c2r64: c is NULL."));
return FALSE;
}

if (0 <= i && i <= 25)
*c = TEXT('A') + i;
else if (26 <= i && i <= 51)
*c = TEXT('a') + i - 26;
else if (52 <= i && i <= 61)
*c = TEXT('0') + i - 52;
else if (i == 62)
*c = TEXT('+');
else if (i == 63)
*c = TEXT('_');
else {
ErrorMsg(TEXT("c2r64: c is not between 0 and 63."));
return FALSE;
}
return TRUE;
}

/************************************************************************\
* SHAtoRadix64 takes as input a SHA_SIZE-byte hash of the full
* pathname of a file. It produces a 27-byte "Radix64"
* representation of the hash, where each byte in the
* "Radix64" representation is between 0 and 2^6.
* It pretends that the SHZ_SIZE + 1st input byte is NULL.
\************************************************************************/

BOOL SHAtoRadix64(LPBYTE pbPathHash, LPTSTR szFileName) {
INT i, j;
TCHAR c1 = 0, c2 = 0, c3 = 0, c4 = 0;

if (!pbPathHash) {
ErrorMsg(TEXT("SHAtoRadix64: pbPathHash is NULL."));
return FALSE;
}

if (!szFileName) {
ErrorMsg(TEXT("SHAtoRadix64: szFileName is NULL."));
return FALSE;
}

for (i = 0, j = 0; i < 18 && j < 24; i += 3, j += 4) {
c1 = c1 | (((pbPathHash[i + 0] >> 7) & 1) << 5);
c1 = c1 | (((pbPathHash[i + 0] >> 6) & 1) << 4);
c1 = c1 | (((pbPathHash[i + 0] >> 5) & 1) << 3);
c1 = c1 | (((pbPathHash[i + 0] >> 4) & 1) << 2);
c1 = c1 | (((pbPathHash[i + 0] >> 3) & 1) << 1);
c1 = c1 | (((pbPathHash[i + 0] >> 2) & 1) << 0);

c2 = c2 | (((pbPathHash[i + 0] >> 1) & 1) << 5);
c2 = c2 | (((pbPathHash[i + 0] >> 0) & 1) << 4);
c2 = c2 | (((pbPathHash[i + 1] >> 7) & 1) << 3);
c2 = c2 | (((pbPathHash[i + 1] >> 6) & 1) << 2);
c2 = c2 | (((pbPathHash[i + 1] >> 5) & 1) << 1);
c2 = c2 | (((pbPathHash[i + 1] >> 4) & 1) << 0);

c3 = c3 | (((pbPathHash[i + 1] >> 3) & 1) << 5);
c3 = c3 | (((pbPathHash[i + 1] >> 2) & 1) << 4);
c3 = c3 | (((pbPathHash[i + 1] >> 1) & 1) << 3);
c3 = c3 | (((pbPathHash[i + 1] >> 0) & 1) << 2);
c3 = c3 | (((pbPathHash[i + 2] >> 7) & 1) << 1);
c3 = c3 | (((pbPathHash[i + 2] >> 6) & 1) << 0);

c4 = c4 | (((pbPathHash[i + 2] >> 5) & 1) << 5);
c4 = c4 | (((pbPathHash[i + 2] >> 4) & 1) << 4);
c4 = c4 | (((pbPathHash[i + 2] >> 3) & 1) << 3);
c4 = c4 | (((pbPathHash[i + 2] >> 2) & 1) << 2);
c4 = c4 | (((pbPathHash[i + 2] >> 1) & 1) << 1);
c4 = c4 | (((pbPathHash[i + 2] >> 0) & 1) << 0);

c2r64(c1, &(szFileName[j + 0]));
c2r64(c2, &(szFileName[j + 1]));
c2r64(c3, &(szFileName[j + 2]));
c2r64(c4, &(szFileName[j + 3]));

c1 = 0; c2 = 0; c3 = 0; c4 = 0;
}

c1 = c1 | (((pbPathHash[18] >> 7) & 1) << 5);
c1 = c1 | (((pbPathHash[18] >> 6) & 1) << 4);
c1 = c1 | (((pbPathHash[18] >> 5) & 1) << 3);
c1 = c1 | (((pbPathHash[18] >> 4) & 1) << 2);
c1 = c1 | (((pbPathHash[18] >> 3) & 1) << 1);
c1 = c1 | (((pbPathHash[18] >> 2) & 1) << 0);

c2 = c2 | (((pbPathHash[18] >> 1) & 1) << 5);
c2 = c2 | (((pbPathHash[18] >> 0) & 1) << 4);
c2 = c2 | (((pbPathHash[19] >> 7) & 1) << 3);
c2 = c2 | (((pbPathHash[19] >> 6) & 1) << 2);
c2 = c2 | (((pbPathHash[19] >> 5) & 1) << 1);
c2 = c2 | (((pbPathHash[19] >> 4) & 1) << 0);

c3 = c3 | (((pbPathHash[19] >> 3) & 1) << 5);
c3 = c3 | (((pbPathHash[19] >> 2) & 1) << 4);
c3 = c3 | (((pbPathHash[19] >> 1) & 1) << 3);
c3 = c3 | (((pbPathHash[19] >> 0) & 1) << 2);
c3 = c3 | (((0              >> 7) & 1) << 1);
c3 = c3 | (((0              >> 6) & 1) << 0);

c2r64(c1, &(szFileName[24]));
c2r64(c2, &(szFileName[25]));
c2r64(c3, &(szFileName[26]));

szFileName[27] = TEXT('\0');

return TRUE;
}

/************************************************************************\
* Exit1 and Exit2 close handles, etc. for CFILEREncryptFile,
* CFILERDecryptFile, SignFile, and VerifyFile.
\************************************************************************/

void Exit1(HWND hWnd, LPTSTR message, HANDLE hFile1, HANDLE hFile2) {
CloseHandle(hFile1);
CloseHandle(hFile2);
MessageBox(hWnd, message, NULL, MB_OK);
return;
}

void Exit2(HWND hWnd, LPTSTR message, HWND hwndDlg, HANDLE hFile1, HANDLE hFile2) {
CloseHandle(hFile1);
CloseHandle(hFile2);
EndDialog(hwndDlg, FALSE);
MessageBox(hWnd, message, NULL, MB_OK);
return;
}

/************************************************************************\
* CFILEREncryptFile()
*
* input:
* hWnd - HWND specified by caller
* szFileName - buffer containing full pathname of file to encrypt
*
* Opens a file to encrypt and saves the file in
* encrypted form in the same path as the plaintext file.
* It is saved as a hidden file with a random filename. Its filename is
* encrypted with fnKey and is stored in the header of the file.
* Deletes the plaintext file.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL CFILEREncryptFile(HWND hWnd, LPTSTR szFileName) {
TCHARszEncryptedFileName[PATH_SIZE];
TCHARszFileName2[PATH_SIZE];
TCHARszRandomName[PATH_SIZE];
TCHARszDirBuf[PATH_SIZE];
BYTEpEncryptBuf[2 * BUF_SIZE];
DWORDdwNumBytesRead = BUF_SIZE;
DWORDdwNumBytesWritten;
DWORDdwEncryptLen = BUF_SIZE;
HWNDhwndDlg;
HCRYPTKEYhFileKey;
HCRYPTKEYphEncryptPubKey;
HCRYPTKEYhfnKey;
BYTE pbFileKeyBlob[BUF_SIZE];
DWORD dwFileBlobLen = BUF_SIZE;
HANDLEhFileRead = INVALID_HANDLE_VALUE;
HANDLEhFileWrite = INVALID_HANDLE_VALUE;
HCRYPTHASHhHash;
DWORDdwBufLen = HEADER_SIZE * sizeof(TCHAR);
DWORDdwDataLen = HEADER_SIZE * sizeof(TCHAR);

if (!szFileName) {
MessageBox(hWnd, TEXT("CFILEREncryptFile: szFileName is NULL."), NULL, MB_OK);
return FALSE;
}

hfnKey = GetfnKey(hWnd);

if (!hfnKey) {
MessageBox(hWnd, TEXT("CFILEREncryptFile: GetfnKey failed."), NULL, MB_OK);
return FALSE;
}

SeparatePathName2(szFileName, szDirBuf, szEncryptedFileName);

GetEncryptPublicKey(hWnd, &phEncryptPubKey);
GenRandomName(szRandomName);

// Generate a key to encrypt this file only if the user
// selected the password option

if (!AlgidEncrypt) {
if (RCRYPT_FAILED(CryptCreateHash(hMe, CALG_MD4, 0, 0, &hHash))) {
MessageBox(hWnd, TEXT("CFILEREncryptFile: CryptCreateHash failed."), NULL, MB_OK);
return FALSE;
}

if (RCRYPT_FAILED(CryptHashData(hHash, (LPBYTE)sPass, lstrlen(sPass), 0))) {
MessageBox(hWnd, TEXT("CFILEREncryptFile: CryptHashData failed."), NULL, MB_OK);
return FALSE;
}

if (RCRYPT_FAILED(CryptDeriveKey(hMe, CALG_RC2, hHash, CRYPT_EXPORTABLE, &hFileKey))) {
MessageBox(hWnd, TEXT("CFILEREncryptFile: CryptDeriveKey failed."), NULL, MB_OK);
return FALSE;
}

CryptDestroyHash(hHash);
}
// Generate a key to encrypt this file only (not derived from password).
else if (RCRYPT_FAILED(CryptGenKey(hMe, AlgidEncrypt, CRYPT_EXPORTABLE, &hFileKey))) {
MessageBox(hWnd, TEXT("CFILEREncryptFile: CryptGenKey failed."), NULL, MB_OK);
return FALSE;
}

dwFileBlobLen = MAXBLOBLEN;

//Export the key so that it can be stored on the disk.

if (RCRYPT_FAILED(CryptExportKey(hFileKey,
  phEncryptPubKey,
  SIMPLEBLOB,
  0,
  pbFileKeyBlob,
  &dwFileBlobLen))) {
MessageBox(hWnd, TEXT("CFILEREncryptFile: CryptExportKey failed."), NULL, MB_OK);
return FALSE;
}

   if (!AlgidEncrypt)
   dwFileBlobLen = 0;

   // Open a file to encrypt

hFileRead = CreateFile(szFileName,
   GENERIC_READ,
   0,
   0,
   OPEN_EXISTING,
   FILE_ATTRIBUTE_READONLY,
   0);

if (hFileRead == INVALID_HANDLE_VALUE) {
MessageBox(hWnd, TEXT("CFILEREncryptFile: CreateFile failed."), NULL, MB_OK);
return FALSE;
}

lstrcpy(szFileName2, szDirBuf);
lstrcat(szFileName2, TEXT("\\"));
lstrcat(szFileName2, szRandomName);

// Create a hidden .CRP file with a random filename.

hFileWrite = CreateFile(szFileName2,
GENERIC_READ | GENERIC_WRITE,
0,
0,
CREATE_NEW,
FILE_ATTRIBUTE_HIDDEN,
0);

if (hFileWrite == INVALID_HANDLE_VALUE) {
MessageBox(hWnd, TEXT("Cannot write to media."), NULL, MB_OK);
CloseHandle(hFileRead);
return FALSE;

}

// Tell the user that the program is busy encrypting data
hwndDlg = CreateDialog(ghModule, TEXT("ENCRYPTING"), hWnd, CryptDlgProc);

if (!hwndDlg) {
Exit2(hWnd, TEXT("CFILEREncryptFile: CreateDialog failed."), hwndDlg, hFileRead, hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

if (RCRYPT_FAILED(CryptEncrypt(hfnKey, 0, TRUE, 0, (LPBYTE)szEncryptedFileName, &dwDataLen, dwBufLen))) {
Exit2(hWnd, TEXT("CFILEREncryptFile: CryptEncrypt failed."), hwndDlg, hFileRead, hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

// Write the original file's encrypted name to disk.

if (RCRYPT_FAILED(WriteFile(hFileWrite,
      szEncryptedFileName,
      HEADER_SIZE * sizeof(*szEncryptedFileName),

&dwNumBytesWritten,
      0))) {
Exit2(hWnd, TEXT("CFILEREncryptFile: WriteFile failed."), hwndDlg, hFileRead, hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

// Write the key blob length to disk.

if (!WriteFile(hFileWrite, &dwFileBlobLen, sizeof(dwFileBlobLen), &dwNumBytesWritten, 0)) {
Exit2(hWnd, TEXT("CFILEREncryptFile: WriteFile failed."), hwndDlg, hFileRead, hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

// Write the key blob to disk.

if (dwFileBlobLen && !WriteFile(hFileWrite,
pbFileKeyBlob,
dwFileBlobLen,
&dwNumBytesWritten,
0)) {
Exit2(hWnd, TEXT("CFILEREncryptFile: WriteFile failed."), hwndDlg, hFileRead, hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

// Encrypt the file by reading small amounts
// of data from the disk, encrypting those Bufs, and saving the
// encrypted Bufs to disk. The loop terminates as soon as there is
// not enough data left on the disk to read a full Buf.

for ( ; ; ) {

if (!ReadFile(hFileRead, pEncryptBuf, BUF_SIZE, &dwNumBytesRead, 0)) {
Exit2(hWnd,
TEXT("CFILEREncryptFile: ReadFile failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

if (dwNumBytesRead != BUF_SIZE)
break;

if (RCRYPT_FAILED(CryptEncrypt(hFileKey, 0, FALSE, 0, pEncryptBuf, &dwEncryptLen, BUF_SIZE))) {
Exit2(hWnd,
TEXT("CFILEREncryptFile: CryptEncrypt failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

if (!WriteFile(hFileWrite, pEncryptBuf, dwEncryptLen, &dwNumBytesWritten, 0)) {
Exit2(hWnd,
TEXT("CFILEREncryptFile: WriteFile failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

dwEncryptLen = BUF_SIZE;
}

// Encrypt the last block of data.

if (0 < dwNumBytesRead && dwNumBytesRead < BUF_SIZE) {
dwEncryptLen = dwNumBytesRead;

if (RCRYPT_FAILED(CryptEncrypt(hFileKey, 0, TRUE, 0, pEncryptBuf, &dwEncryptLen, BUF_SIZE))) {
Exit2(hWnd, TEXT("CryptEncrypt failed."), hwndDlg, hFileRead, hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

if (!WriteFile(hFileWrite, pEncryptBuf, dwEncryptLen, &dwNumBytesWritten, 0)) {
Exit2(hWnd, TEXT("WriteFile failed."), hwndDlg, hFileRead, hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}
}

if (RCRYPT_FAILED(CryptDestroyKey(hFileKey))) {
Exit2(hWnd, TEXT("CryptDestroyKey failed."), hwndDlg, hFileRead,hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

CloseHandle(hFileRead);

if (!DeleteFile(szFileName)) {
EndDialog(hwndDlg, FALSE);
MessageBox(hWnd, TEXT("File cannot be deleted: It is either in use by another process, or it is a system file."), NULL, MB_OK);
CloseHandle(hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

EndDialog(hwndDlg, FALSE);
CloseHandle(hFileWrite);
SetFileAttributes(szFileName2, FILE_ATTRIBUTE_HIDDEN);

return TRUE;
}

/************************************************************************\
* CFILERDecryptFile()
*
* input:
* hWnd - HWND specified by caller
* szFileName - buffer containing full pathname of file to decrypt.
*
* purpose:
* decrypts the file specified by szFileName and stores the file in
* the same directory. Deletes the encrypted file.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL CFILERDecryptFile(HWND hWnd, LPTSTR szFileName)
{
TCHARszFileName2[PATH_SIZE];
TCHARszDecryptedFileName[PATH_SIZE];
TCHARszFile[PATH_SIZE];
BYTEpDecryptBuf[2 * BUF_SIZE];
DWORDdwNumBytesRead;
DWORDdwNumBytesWritten;
DWORDdwDecryptDataLen = BUF_SIZE;
HCRYPTKEYhFileKey;
HCRYPTKEYphEncryptPubKey;
BYTE pbDecryptBlob[BUF_SIZE];
DWORD dwDecryptBlobLen;
HWNDhwndDlg;
HANDLEhFileRead;
HANDLEhFileWrite = INVALID_HANDLE_VALUE;
HCRYPTHASHhHash;
DWORDdwDataLen = HEADER_SIZE * sizeof(TCHAR);
//BYTEpHashData[BUF_SIZE];
DWORDHashDataLen;

if (!szFileName) {
MessageBox(hWnd, TEXT("CFILERDecryptFile: szFileName is NULL."), NULL, MB_OK);
return FALSE;
}

GetEncryptPublicKey(hWnd, &phEncryptPubKey);

GetDecryptedFileName(hWnd,
 szFileName,
 szDecryptedFileName,
 &hFileRead);

// Read the key blob length from the disk.

if (!ReadFile(hFileRead, &dwDecryptBlobLen, sizeof(dwDecryptBlobLen), &dwNumBytesRead, 0)) {
MessageBox(hWnd, TEXT("CFILERDecryptFile: ReadFile failed."), NULL, MB_OK);
CloseHandle(hFileRead);
return FALSE;
}

// Read the key blob from the disk into a Buf.

if (dwDecryptBlobLen && !ReadFile(hFileRead,
  pbDecryptBlob,
  dwDecryptBlobLen,
  &dwNumBytesRead,
  0)) {
MessageBox(hWnd, TEXT("CFILERDecryptFile: ReadFile failed."), NULL, MB_OK);
CloseHandle(hFileRead);
return FALSE;
}

// Import the key whose blob is contained in the Buf pbDecryptBlob

if (dwDecryptBlobLen && !CryptImportKey(hMe,
pbDecryptBlob,
dwDecryptBlobLen,
0,
0,
&hFileKey)) {
MessageBox(hWnd, TEXT("CFILERDecryptFile: CryptImportKey failed."), NULL, MB_OK);
CloseHandle(hFileRead);
return FALSE;
}

SeparatePathName(szFileName, szFileName2, szFile);

lstrcat(szFileName2, TEXT("\\"));
lstrcat(szFileName2, szDecryptedFileName);

// Generate a key to decrypt this file if the user
// selected the password option

if (!dwDecryptBlobLen) {
lstrcpy(szPassName, szFileName2);

if (!GetPass(hWnd)) {
MessageBox(hWnd, TEXT("CFILERDecryptFile: GetPass failed."), NULL, MB_OK);
CloseHandle(hFileRead);
return FALSE;
}

lstrcpy(szPassName, TEXT("\0"));

if (RCRYPT_FAILED(CryptCreateHash(hMe, CALG_MD4, 0, 0, &hHash))) {
MessageBox(hWnd, TEXT("CFILERDecryptFile: CryptCreateHash failed."), NULL, MB_OK);
CloseHandle(hFileRead);
return FALSE;
}

if (RCRYPT_FAILED(CryptHashData(hHash, (LPBYTE)sPass, lstrlen(sPass), 0))) {
MessageBox(hWnd, TEXT("DecrypttFile: CryptHashData failed."), NULL, MB_OK);
return FALSE;
}

HashDataLen = BUF_SIZE;

if (RCRYPT_FAILED(CryptDeriveKey(hMe, CALG_RC2, hHash, CRYPT_EXPORTABLE, &hFileKey))) {
MessageBox(hWnd, TEXT("CFILERDecryptFile: CryptDeriveKey failed."), NULL, MB_OK);
CloseHandle(hFileRead);
return FALSE;
}

CryptDestroyHash(hHash);

dwDecryptBlobLen = 0;
}

// Recreate the plaintext file

hFileWrite = CreateFile(szFileName2,
GENERIC_READ | GENERIC_WRITE,
0,
0,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0);

if (hFileWrite == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_FILENAME_EXCED_RANGE) {
TCHAR szEmergencyName[PATH_SIZE];
TCHAR szEmergencyRandomName[PATH_SIZE];
TCHAR szDir1[PATH_SIZE];
TCHAR szFile1[PATH_SIZE];
SeparatePathName(szFileName, szDir1, szFile1);
GenRandomName2(szEmergencyRandomName);
lstrcpy(szEmergencyName, szDir1);
lstrcat(szEmergencyName, TEXT("\\recovered"));
lstrcat(szEmergencyName, szEmergencyRandomName);
hFileWrite = CreateFile(szEmergencyName, GENERIC_READ | GENERIC_WRITE,
0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFileWrite == INVALID_HANDLE_VALUE) {
ErrorMsg(TEXT("Could not recover file."));
return FALSE;
}
}
else {
MessageBox(hWnd, TEXT("CFILERDecryptFile: CreateFile failed."), NULL, MB_OK);
CloseHandle(hFileRead);
return FALSE;
}
}

// Create a dialog box to inform the user that the program is busy decrypting data.

hwndDlg = CreateDialog(ghModule, TEXT("DECRYPTING"), hWnd, CryptDlgProc);

if (!hwndDlg) {
Exit2(hWnd, TEXT("CFILERDecryptFile: CreateDialog failed."), hwndDlg, hFileRead, hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

// The following for loop decrypts the data in units of BUF_SIZE
// at a time and stores the decrypted data into the file hFileWrite.
// The loop terminates when fewer than BUF_SIZE have been read.

for ( ; ; ) {
if (!ReadFile(hFileRead, pDecryptBuf, BUF_SIZE, &dwNumBytesRead, 0)) {
Exit2(hWnd,
TEXT("CFILERDecryptFile: ReadFile failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

if (dwNumBytesRead != BUF_SIZE)
break;

if (RCRYPT_FAILED(CryptDecrypt(hFileKey, 0, FALSE, 0, pDecryptBuf, &dwDecryptDataLen))) {
Exit2(hWnd,
TEXT("CFILERDecryptFile: CryptDecrypt failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

if (!WriteFile(hFileWrite, pDecryptBuf, dwDecryptDataLen, &dwNumBytesWritten, 0)) {
Exit2(hWnd,
TEXT("CFILERDecryptFile: WriteFile failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

dwDecryptDataLen = BUF_SIZE;
}

// Decrypt the final block of data.

if (0 < dwNumBytesRead && dwNumBytesRead < BUF_SIZE) {
dwDecryptDataLen = dwNumBytesRead;

if (RCRYPT_FAILED(CryptDecrypt(hFileKey, 0, TRUE, 0, pDecryptBuf, &dwDecryptDataLen))) {

MessageBox(hWnd, TEXT("GetfnKey: CryptImportKey failed"), NULL, MB_OK);
Exit2(hWnd,
TEXT("CFILERDecryptFile: CryptDecrypt failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

if (!WriteFile(hFileWrite, pDecryptBuf, dwDecryptDataLen, &dwNumBytesWritten, 0)) {
Exit2(hWnd,
TEXT("CFILERDecryptFile: WriteFile failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}
}

if (!CryptDestroyKey(hFileKey)) {
Exit2(hWnd,
TEXT("CFILERDecryptFile: CryptDestroyKey failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

EndDialog(hwndDlg, FALSE);
CloseHandle(hFileRead);
CloseHandle(hFileWrite);

if (!DeleteFile(szFileName)) {
MessageBox(hWnd, TEXT("CFILERDecryptFile: DeleteFile failed."), NULL, MB_OK);
DeleteFile(szFileName2);
}

return TRUE;
}

/************************************************************************\
* SignFile:
* Opens a file, creates a hash value of the file,
* signs that hash value, and stores the result to disk.
\************************************************************************/

BOOL SignFile(HWND hWnd, LPTSTR szFileName) {
TCHAR szFilter1[] = TEXT("Signature Files (*.SGN)\0*.SGN\0");
TCHAR szFilter2[] = TEXT("All Files (*.*)\0*.*\0");
TCHARszFile[PATH_SIZE] = TEXT("\0");
TCHARszFile2[PATH_SIZE] = TEXT("\0");
TCHARszFileTitle[PATH_SIZE];
TCHARszFileName2[PATH_SIZE];
DWORDdwNumBytesRead = BUF_SIZE;
DWORDdwNumBytesWritten;
HWNDhwndDlg;
HANDLEhFileRead = INVALID_HANDLE_VALUE;
HANDLEhFileWrite = INVALID_HANDLE_VALUE;
HCRYPTKEYphSignaturePubKey;
HCRYPTHASHhHash;
BYTEpSignBuf[BUF_SIZE];
BYTEpbHash[BUF_SIZE];
DWORDdwHashLen;

GetSignaturePublicKey(hWnd, &phSignaturePubKey);

if (!szFileName) {
MessageBox(hWnd, TEXT("SignFile: szFileName is NULL."), NULL, MB_OK);
return FALSE;
}

lstrcpy(szFile, TEXT(""));
lstrcpy(szFileTitle, TEXT(""));

// Open plaintext file

hFileRead = CreateFile(szFileName,
   GENERIC_READ,
   0,
   0,
   OPEN_EXISTING,
   FILE_ATTRIBUTE_READONLY,
   0);

if (hFileRead == INVALID_HANDLE_VALUE) {
MessageBox(hWnd, TEXT("SignFile: CreateFile failed."), NULL, MB_OK);
return FALSE;
}

// hash the full pathname of the file. This will be the name of
// the signature file.

if (!GetSigFileName(szFileName, szFileName2)) {
MessageBox(hWnd, TEXT("SignFile: GetSigFileName failed."), NULL, MB_OK);
return FALSE;
}

// Create signature file.

if (GetFileAttributes(szFileName2) == 0xFFFFFFFF) {

hFileWrite = CreateFile(szFileName2,
GENERIC_READ | GENERIC_WRITE,
0,
0,
CREATE_NEW,
FILE_ATTRIBUTE_HIDDEN,
0);

if (hFileWrite == INVALID_HANDLE_VALUE) {
Exit1(hWnd, TEXT("SignFile: CreateFile failed."), hFileRead, hFileWrite);
return FALSE;
}
}
else {
if (MessageBox(hWnd,
   TEXT("Delete signature?"),
   TEXT("Signature file already exists."),
   MB_OKCANCEL) == IDOK) {

DeleteFile(szFileName2);

hFileWrite = CreateFile(szFileName2,
GENERIC_READ | GENERIC_WRITE,
0,
0,
CREATE_NEW,
FILE_ATTRIBUTE_HIDDEN,
0);

if (hFileWrite == INVALID_HANDLE_VALUE) {
Exit1(hWnd, TEXT("SignFile: CreateFile failed."), hFileRead, hFileWrite);
return FALSE;
}
}
else {
CloseHandle(hFileRead);

return FALSE;
}
}

// Create a dialog box to inform the user that the program is hashing data.

hwndDlg = CreateDialog(ghModule, TEXT("SIGNING"), hWnd, CryptDlgProc);

if (RCRYPT_FAILED(CryptCreateHash(hMe, AlgidSign, 0, 0, &hHash))) {
Exit2(hWnd, TEXT("SignFile: CryptCreateHash failed."), hwndDlg, hFileRead, hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

// The following for loop hashes the file in BUF_SIZE Bufs at a time.

for (dwNumBytesRead = BUF_SIZE ; dwNumBytesRead == BUF_SIZE; ) {
if (!ReadFile(hFileRead, pSignBuf, BUF_SIZE, &dwNumBytesRead, 0)) {
Exit2(hWnd,
TEXT("SignFile: ReadFile failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}
else if (RCRYPT_FAILED(CryptHashData(hHash, pSignBuf, dwNumBytesRead, 0))) {
Exit2(hWnd,
TEXT("SignFile: CryptHashData failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}
}

dwHashLen = BUF_SIZE;

if (RCRYPT_FAILED(CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, pbHash, &dwHashLen)))
{

MessageBox(hWnd, TEXT("SignFile: CryptSignHash failed"), NULL, MB_OK);

Exit2(hWnd,
TEXT("SignFile: CryptSignHash failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

// Write ALG_ID to disk

if (!WriteFile(hFileWrite, &AlgidSign, sizeof(ALG_ID), &dwNumBytesWritten, 0)) {
Exit2(hWnd, TEXT("SignFile: WriteFile failed."), hwndDlg, hFileRead, hFileWrite);
return FALSE;
}

// Write hash to disk.

if (!WriteFile(hFileWrite, pbHash, dwHashLen, &dwNumBytesWritten, 0)) {
Exit2(hWnd,
TEXT("SignFile: WriteFile failed."),
hwndDlg,
hFileRead,
hFileWrite);
DeleteFile(szFileName2);
return FALSE;
}

CryptDestroyHash(hHash);
EndDialog(hwndDlg, FALSE);
CloseHandle(hFileRead);
CloseHandle(hFileWrite);

return TRUE;
}

/************************************************************************\
* VerifyFile
* Opens a signature file and the original file.
* Hashes the original file and tries to verify
* the hash.
\************************************************************************/

BOOL VerifyFile(HWND hWnd, LPTSTR szFileName) {
TCHAR szFilter1[] = TEXT("Signature Files (*.SGN)\0*.SGN\0");
TCHAR szFilter2[] = TEXT("All Files (*.*)\0*.*\0");
TCHARszFile[PATH_SIZE] = TEXT("\0");
TCHARszFile2[PATH_SIZE] = TEXT("\0");
TCHARszFileTitle[PATH_SIZE];
TCHARszFileName2[PATH_SIZE];
DWORDdwNumBytesRead;
DWORDdwNumBytesRead2;
HWNDhwndDlg;
HANDLEhFileOrig = INVALID_HANDLE_VALUE;
HANDLEhFileSig = INVALID_HANDLE_VALUE;
BYTEpbSig[BUF_SIZE];
BYTEpVerifyBuf[BUF_SIZE];
DWORDdwSigLen;
HCRYPTHASHhHash;
HCRYPTKEYphSignaturePubKey;
ALG_ID DiskAlgid;

if (!szFileName) {
MessageBox(hWnd, TEXT("VerifyFile: szFileName is NULL."), NULL, MB_OK);
return FALSE;
}

GetSignaturePublicKey(hWnd, &phSignaturePubKey);

// hash the full pathname of the file to determine the signature file
// to open.

if (!GetSigFileName(szFileName, szFileName2)) {
MessageBox(hWnd, TEXT("VerifyFile: GetSigFileName failed."), NULL, MB_OK);
return FALSE;
}

lstrcpy(szFile, TEXT(""));
lstrcpy(szFileTitle, TEXT(""));

// Open the plaintext file to hash it.

hFileOrig = CreateFile(szFileName,
   GENERIC_READ,
   0,
   0,
   OPEN_EXISTING,
   FILE_ATTRIBUTE_READONLY,
   0);

if (hFileOrig == INVALID_HANDLE_VALUE) {
MessageBox(hWnd, TEXT("VerifyFile: CreateFile failed."), NULL, MB_OK);
CloseHandle(hFileOrig);
return FALSE;
}

// Automatically open the signature file if it
// exists in the same directory as the file to be verified.
// Otherwise, prompt the user to browse the directory tree
// for the signature file.

if (GetFileAttributes(szFileName2) != 0xFFFFFFFF) {

hFileSig = CreateFile(szFileName2,
  GENERIC_READ,
  0,
  0,
  OPEN_EXISTING,
  FILE_ATTRIBUTE_READONLY,
  0);

if (hFileSig == INVALID_HANDLE_VALUE) {
Exit1(hWnd, TEXT("VerifyFile: CreateFile failed."), hFileOrig, hFileSig);
return FALSE;
}
}
else {
Exit1(hWnd, TEXT("Signature file does not exist."), hFileOrig, hFileSig);
return FALSE;
}

// Create a dialog box to tell the user that the program is busy hashing data.

hwndDlg = CreateDialog(ghModule, TEXT("VERIFYING"), hWnd, CryptDlgProc);

// Read the ALG_ID

if (!ReadFile(hFileSig, &DiskAlgid, sizeof(ALG_ID), &dwNumBytesRead2, 0)) {
Exit2(hWnd, TEXT("VerifyFile: ReadFile failed."), hwndDlg, hFileOrig, hFileSig);
return FALSE;
}

// Read the hash

if (!ReadFile(hFileSig, pbSig, BUF_SIZE, &dwSigLen, 0)) {
Exit2(hWnd, TEXT("VerifyFile: ReadFile failed."), hwndDlg, hFileOrig, hFileSig);
return FALSE;
}

if (RCRYPT_FAILED(CryptCreateHash(hMe, DiskAlgid, 0, 0, &hHash))) {
Exit2(hWnd, TEXT("VerifyFile: CryptCreateHash failed."), hwndDlg, hFileOrig, hFileSig);
return FALSE;
}

// hash the file in BUF_SIZE chunks

for (dwNumBytesRead = BUF_SIZE; dwNumBytesRead == BUF_SIZE; ) {
if (!ReadFile(hFileOrig, pVerifyBuf, BUF_SIZE, &dwNumBytesRead, 0)) {
Exit2(hWnd, TEXT("VerifyFile: ReadFile failed."), hwndDlg, hFileOrig, hFileSig);
return FALSE;
}
if (RCRYPT_FAILED(CryptHashData(hHash, pVerifyBuf, dwNumBytesRead, 0))) {
Exit2(hWnd,
TEXT("VerifyFile: CryptHashData failed."),
hwndDlg,
hFileOrig,
hFileSig);
return FALSE;
}
}

if (RCRYPT_FAILED(CryptVerifySignature(hHash, pbSig, dwSigLen, phSignaturePubKey, 0, 0))) {
EndDialog(hwndDlg, FALSE);
MessageBox(hWnd,
   TEXT("Could not verify signature."),
   TEXT("Result"),
   MB_OK | MB_ICONINFORMATION);
CloseHandle(hFileOrig);
CloseHandle(hFileSig);
return TRUE;
}

if (RCRYPT_FAILED(CryptDestroyHash(hHash))) {
Exit2(hWnd,
TEXT("Could not destroy hash."),
hwndDlg,
hFileOrig,
hFileSig);
return FALSE;
}

EndDialog(hwndDlg, FALSE);

MessageBox(hWnd,
   TEXT("Signature verified."),
   TEXT("Result"),
   MB_OK | MB_ICONINFORMATION);
CloseHandle(hFileOrig);
CloseHandle(hFileSig);

return TRUE;
}

/************************************************************************\
* EncryptDir()
*
* in parameters
* hWnd - HWND specified by caller
* szDirName - full path of directory to encrypt
* lpCInfo - instance data for drive child
*
* purpose:
* Encrypts szDirName and recursively encrypts all of its
* subdirectories. Confirmation is needed for each subdirectory.
* A random directory name is generated, and szDirName is
* renamed and given hidden attributes.
* dirinfo contains the fnKey-encrypted directory name.
*
* returns
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL EncryptDir(HWND hWnd, LPTSTR szDirName, LPCINFO lpCInfo) {
TCHARszDirInfo[PATH_SIZE];
TCHARszFileName[PATH_SIZE];
TCHARszEncryptedDirName[PATH_SIZE];
TCHARszRandom[PATH_SIZE];
TCHARszRandomName[PATH_SIZE];
TCHARszDir[PATH_SIZE];
TCHARszFile[PATH_SIZE];
TCHARszMessage[PATH_SIZE];
TCHARszOldDir[PATH_SIZE];
TCHARBuf[PATH_SIZE];
TCHARszDirName2[PATH_SIZE];
LPTSTRlpszDirName2;
HANDLE hFileDirInfo;
HANDLEhFindFile;
WIN32_FIND_DATAFindFileData;
BOOLfNextFile;
DWORDdwLastError;
DWORDdwDataLen = BUF_SIZE;
DWORDdwNumBytesWritten;
LONGlIndex;
LONGlCount;
HCRYPTKEYhfnKey;

if (!szDirName) {
MessageBox(hWnd, TEXT("EncryptDir: szDirName is NULL."), NULL, MB_OK);
return FALSE;
}

if (!lpCInfo) {
MessageBox(hWnd, TEXT("EncryptDir: lpCInfo is NULL."), NULL, MB_OK);
return FALSE;
}

hfnKey = GetfnKey(hWnd);

if (!hfnKey) {
MessageBox(hWnd, TEXT("EncryptDir: GetfnKey failed."), NULL, MB_OK);
return FALSE;
}

SeparatePathName2(szDirName, szDir, szEncryptedDirName);

lstrcpy(szMessage, TEXT("\0"));
wsprintf(szMessage, TEXT("Encrypt the directory %s?\n"), szDirName);

// ask for confirmation about encrypting the directory

if (MessageBox(hWnd, szMessage,
TEXT("Confirmation needed."), MB_OKCANCEL) == IDCANCEL)
return FALSE;

lstrcpy(szFileName, szDir);
lstrcat(szFileName, TEXT("\\"));
lstrcat(szFileName, szEncryptedDirName);

if (!szDirName) {
MessageBox(hWnd, TEXT("EncryptDir: szDirName is NULL."), NULL, MB_OK);
return FALSE;
}

GenRandomName(szRandom);

lstrcpy(szRandomName, szDir);
lstrcat(szRandomName, TEXT("\\"));
lstrcat(szRandomName, szRandom);
lstrcpy(szDirInfo, szFileName);
lstrcat(szDirInfo, TEXT("\\dirinfo"));

// Create a file called "dirinfo" in directory about to encrypted.
// This will contain the original directory name encrypted with fnKey.

hFileDirInfo = CreateFile(szDirInfo,
  GENERIC_READ | GENERIC_WRITE,
  0,
  0,
  CREATE_ALWAYS,
  FILE_ATTRIBUTE_HIDDEN,
  0);

if (hFileDirInfo == INVALID_HANDLE_VALUE) {
MessageBox(hWnd, TEXT("EncryptDir: CreateFile failed."), NULL, MB_OK);
return FALSE;

}

// Encrypt the directory name with fnKey.

if (RCRYPT_FAILED(CryptEncrypt(hfnKey,
  0,
  TRUE,
  0,
  (LPBYTE)szEncryptedDirName,
  &dwDataLen,
  HEADER_SIZE * sizeof(*szEncryptedDirName)))) {
MessageBox(hWnd, TEXT("EncryptDir: CryptEncrypt failed."), NULL, MB_OK);
CloseHandle(hFileDirInfo);
return FALSE;
}

// Write the original directory's encrypted name to disk.

if (!WriteFile(hFileDirInfo,
   szEncryptedDirName,
   HEADER_SIZE * sizeof(*szEncryptedDirName),
   &dwNumBytesWritten,
   0)) {
MessageBox(hWnd, TEXT("EncryptDir: WriteFile failed."), NULL, MB_OK);
CloseHandle(hFileDirInfo);
return FALSE;
}

CloseHandle(hFileDirInfo);

SeparatePathName2(szDirName, szDir, szFile);

lstrcpy(szDirName, szDir);
lstrcat(szDirName, TEXT("\\"));
lstrcat(szDirName, szFile);
lstrcpy(szOldDir, szDirName);
lstrcat(szDirName, TEXT("\\*"));

if ((hFindFile = FindFirstFile(szDirName, &FindFileData))
== INVALID_HANDLE_VALUE) {
MessageBox(hWnd, TEXT("EncryptDir: FindFirstFile failed."), NULL, MB_OK);
return FALSE;
}

// Loop through, calling FindNextFiles until no more files are left.

do {

lstrcpy(szFileName, szDir);
lstrcat(szFileName, TEXT("\\"));
lstrcat(szFileName, szFile);
lstrcat(szFileName, TEXT("\\"));
lstrcat(szFileName, FindFileData.cFileName);

// is szFileName encrypted and not .., ., or dirinfo?
if (!IsEncrypted(szFileName)
   && lstrcmp(FindFileData.cFileName, TEXT("."))
   && lstrcmp(FindFileData.cFileName, TEXT(".."))
   && lstrcmp(FindFileData.cFileName, TEXT("dirinfo"))) {
// Is szFileName a directory?
if (CFilerIsDirectory(szFileName)) {
// Encrypt the directory szFileName.
if (!EncryptDir(hWnd, szFileName, lpCInfo)) {
MessageBox(hWnd, TEXT("EncryptDir: EncryptDir failed."), NULL, MB_OK);
return FALSE;
}
}
// encrypt the file szFileName.
else if (!CFILEREncryptFile(hWnd, szFileName)) {
MessageBox(hWnd, TEXT("EncryptDir: CFILEREncryptFile failed."), NULL, MB_OK);
return FALSE;
}
}

fNextFile = FindNextFile(hFindFile, &FindFileData);

dwLastError = GetLastError();

} while (fNextFile || dwLastError != ERROR_NO_MORE_FILES);

FindClose(hFindFile);

lpszDirName2 = szDirName2;

// Collapse the selected directory.

lIndex = GetLBText(lpCInfo->hDirLB, Buf);
lCount = GetDirDepth(Buf, &lpszDirName2);
CollapseDir(lpCInfo, lIndex, lCount);

// rename the directory with a random name.
#ifdef UNICODE
if (_wrename((const wchar_t *)szOldDir, (const wchar_t *)szRandomName)) {
#else
if (rename(szOldDir, szRandomName)) {
#endif
MessageBox(hWnd, TEXT("Another process is using this directory."), NULL, MB_OK);
return FALSE;
}

// hide the directory.

SetFileAttributes(szRandomName, FILE_ATTRIBUTE_HIDDEN);

return TRUE;
}

/************************************************************************\
* GenRandomName()
*
* out parameters
* szRandomName - buffer to receive random filename
*
* purpose:
* Uses the random number generator to generate a random filename with
* a .CRP extension.
*
* returns
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL GenRandomName(LPTSTR szRandomName) {
INT i;

if (!szRandomName) {
ErrorMsg(TEXT("GenRandomName: szRandomName is NULL."));
return FALSE;
}

for (i = 0; i < RANDOM_NAME_LENGTH; i++)
szRandomName[i] = rand() % 26 + TEXT('a');
szRandomName[i] = TEXT('\0');

lstrcat(szRandomName, TEXT(".CRP\0"));

return TRUE;
}

/************************************************************************\
* GenRandomName2()
*
* out parameters
* szRandomName - buffer to receive random filename
*
* purpose:
* Uses the random number generator to generate a random filename without
* a .CRP extension.
*
* returns
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL GenRandomName2(LPTSTR szRandomName) {
INT i;

if (!szRandomName) {
ErrorMsg(TEXT("GenRandomName: szRandomName is NULL."));
return FALSE;
}

for (i = 0; i < RANDOM_NAME_LENGTH; i++)
szRandomName[i] = rand() % 26 + TEXT('a');
szRandomName[i] = TEXT('\0');

return TRUE;
}

/************************************************************************\
* GetSigFileName()
*
* input
* in parameters:
* szFileName - full path of filename
* out parameters:
* szOutName - buffer containing name of signature file.
*
* purpose:
* obtains the name of the signature file that corresponds to szFileName.
* by hashing it and converting it to ascii.
*
* returns
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL GetSigFileName(LPTSTR szFileName, LPTSTR szOutName) {
HCRYPTHASH hHash;
BYTE pbHash[BUF_SIZE];
DWORD dwHashLen;
TCHAR szFileBuf[PATH_SIZE];
TCHAR szSigDir[PATH_SIZE];

if (!szFileName) {
ErrorMsg(TEXT("GetSigFileName: szFileName is NULL."));
return FALSE;
}

if (!szOutName) {
ErrorMsg(TEXT("GetSigFileName: szOutName is NULL."));
return FALSE;
}

 GetSigDir(szSigDir);

// Hash szFileName.

if (RCRYPT_FAILED(CryptCreateHash(hMe, CALG_SHA, 0, 0, &hHash))) {
ErrorMsg(TEXT("GetSigFileName: CryptCreateHash failed."));
return FALSE;
}

if (RCRYPT_FAILED(CryptHashData(hHash, (LPBYTE)szFileName, lstrlen(szFileName) * sizeof(*szFileName), 0))) {
ErrorMsg(TEXT("GetSigFileName: CryptHashData failed."));
return FALSE;
}

dwHashLen = SHA_SIZE;

if (RCRYPT_FAILED(CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &dwHashLen, 0))) {
ErrorMsg(TEXT("GetSigFileName: CryptFinishHash failed."));

return FALSE;
}

// Convert the hash to ascii

if (!SHAtoRadix64(pbHash, szFileBuf)) {
ErrorMsg(TEXT("DrvWndProc: FILLFILE: SHAtoRadix64 failed."));
return 0;
}

lstrcpy(szOutName, szSigDir);
lstrcat(szOutName, TEXT("\\"));
lstrcat(szOutName, szFileBuf);

CryptDestroyHash(hHash);
}

/************************************************************************\
* HasSignature()
*
* in parameters
* szFileName - full path of filename in question
* sigtable - pointer to a string table with displayed fields containing
* the filenames of all the files in the \sig directory, which is located
* on the same drive as the system directory.
*
* out parameters
* bHasSignature - flag specifying whether szFileName has a signature
*
* purpose:
* determines whether \sig\szFileName has a signature by calling
* GetSigFileName.
*
* returns
* TRUE if such a file exists
* FALSE otherwise or if unsuccessful
\************************************************************************/

BOOL HasSignature(LPTSTR szFileName, TABLE sigtable, BOOL *bHasSignature) {
TCHAR szFileName2[PATH_SIZE];

if (!szFileName) {
ErrorMsg(TEXT("HasSignature: szFileName is NULL."));
return FALSE;
}

if (!bHasSignature) {
ErrorMsg(TEXT("HasSignature: bHasSignature is NULL."));
return FALSE;
}

if (!GetSigFileName(szFileName, szFileName2)) {
ErrorMsg(TEXT("HasSignature: GetSigFileName failed."));
return FALSE;
}

// Does this file exist?

if (TableFind(sigtable, szFileName2))
*bHasSignature = TRUE;
else
*bHasSignature = FALSE;
}

/************************************************************************\
* GetDecryptedFileName()
*
* in parameters
* hWnd - HWND specified by caller
* szFileName - full path of file
*
* out parameters
* szDecryptedFileName - buffer to hold full decrypted path
* hFileRead - pointer to file handle
*
* purpose:
* reads HEADER_SIZE bytes from szFileName, decrypts it with fnKey, and
* stores the result on szDecryptedFileName, the decrypted file name.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL GetDecryptedFileName(HWND hWnd,
  LPTSTR szFileName,
  LPTSTR szDecryptedFileName,
  HANDLE *hFileRead) {
DWORD dwDataLen = HEADER_SIZE * sizeof(TCHAR);
DWORD dwNumBytesRead;
TCHAR szDir[PATH_SIZE];
HCRYPTKEY hfnKey;

if (!szFileName) {
MessageBox(hWnd, TEXT("GetDecryptedFileName: szFileName is NULL."), NULL, MB_OK);
return FALSE;
}

if (!szDecryptedFileName) {
MessageBox(hWnd, TEXT("GetDecryptedFileName: szDecryptedFileName is NULL."), NULL, MB_OK);
return FALSE;
}

hfnKey = GetfnKey(hWnd);

if (!hfnKey) {
MessageBox(hWnd, TEXT("GetDecryptedFileName: GetfnKey failed."), NULL, MB_OK);
return FALSE;
}

// Open encrypted file.

*hFileRead = CreateFile(szFileName,
   GENERIC_READ,
   0,
   0,
   OPEN_EXISTING,
   GetFileAttributes(szFileName),
   0);

if (*hFileRead == INVALID_HANDLE_VALUE) {
MessageBox(hWnd, TEXT("GetDecryptedFileName: CreateFile failed."), NULL, MB_OK);
return FALSE;
}

// Read the encrypted filename from the disk.

if (!ReadFile(*hFileRead,
   szDecryptedFileName,
   HEADER_SIZE * sizeof(*szDecryptedFileName),
   &dwNumBytesRead,
   0))
return FALSE;

if (dwNumBytesRead != HEADER_SIZE * sizeof(*szDecryptedFileName)) {
SeparatePathName2(szFileName, szDir, szDecryptedFileName);
return FALSE;
}

// Decrypt the filename.

CryptDecrypt(hfnKey, 0, TRUE, 0, (LPBYTE)szDecryptedFileName, &dwDataLen);

return TRUE;
}

/************************************************************************\
* GetDecryptedDirName()
*
* in parameters
* hWnd - HWND specified by caller
* szDirectoryName - full path of directory
* bDeldirinfo - flag: TRUE means caller wants dirinfo to be deleted
*
* out parameters
* szDecryptedDirName - buffer to hold full decrypted pathname of
* szDirName
* hFileRead - pointer to file handle
*
* purpose:
* reads HEADER_SIZE bytes from file dirinfo in directory szDirectoryName,
* decrypts it with fnKey, and stores the result in szDecryptedDirName,
* a buffer to hold the decrypted directory name.
*
* returns
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL GetDecryptedDirName(HWND hWnd,
   LPTSTR szDirectoryName,
   LPTSTR szDecryptedDirName,
   BOOL bDeldirinfo,
   HANDLE *hFileRead) {
DWORD dwDataLen = HEADER_SIZE * sizeof(TCHAR);
DWORD dwNumBytesRead;
TCHAR szDir[PATH_SIZE];
TCHAR szBuf[PATH_SIZE];
HCRYPTKEY hfnKey;

hfnKey = GetfnKey(hWnd);

if (!hfnKey) {
MessageBox(hWnd, TEXT("GetDecryptedFileName: GetfnKey failed."), NULL, MB_OK);
return FALSE;
}

if (!szDirectoryName) {
MessageBox(hWnd, TEXT("GetDecryptedDirName: szDirectoryName is NULL."), NULL, MB_OK);
return FALSE;
}

if (!szDecryptedDirName) {
MessageBox(hWnd, TEXT("GetDecryptedDirNAme: szDecryptedDirName is NULL."), NULL, MB_OK);
return FALSE;
}

lstrcpy(szDir, szDirectoryName);

if (szDir[lstrlen(szDir) - 1] != TEXT('\\'))
lstrcat(szDir, TEXT("\\"));

lstrcat(szDir, TEXT("dirinfo"));

// open dirinfo in szDirectoryName.

*hFileRead = CreateFile(szDir,
   GENERIC_READ,
   0,
   0,
   OPEN_EXISTING,
   GetFileAttributes(szDir),
   0);

if (*hFileRead == INVALID_HANDLE_VALUE)
return FALSE;

// Read the encrypted filename from the disk.

if (!ReadFile(*hFileRead,
   szDecryptedDirName,
   HEADER_SIZE * sizeof(*szDecryptedDirName),
   &dwNumBytesRead,
   0))
return FALSE;

CloseHandle(*hFileRead);

// Decrypt the directoryname.

CryptDecrypt(hfnKey, 0, TRUE, 0, (LPBYTE)szDecryptedDirName, &dwDataLen);

// enclose the directory name with brackets.

lstrcpy(szBuf, TEXT("["));
lstrcat(szBuf, szDecryptedDirName);
lstrcat(szBuf, TEXT("]"));
lstrcpy(szDecryptedDirName, szBuf);

if (bDeldirinfo)
DeleteFile(szDir);

return TRUE;
}

/************************************************************************\
* DecryptDir()
*
* in parameters
* hWnd - HWND specified by caller
* szDirName - Full path of directory to be decrypted
* lpCInfo - instance data for drive child
*
* purpose:
* recursively decrypts szDirName and all of its subdirectories.
*
* returns
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL DecryptDir(HWND hWnd, LPTSTR szDirName, LPCINFO lpCInfo) {
INT i, j;
TCHAR szDir[PATH_SIZE];
TCHAR szDir2[PATH_SIZE];
TCHAR szFile[PATH_SIZE];
TCHAR szBuf2[PATH_SIZE];
TCHAR Buf[PATH_SIZE];
TCHAR szDecryptedDirName[PATH_SIZE];
TCHAR szFileName[PATH_SIZE];
TCHAR szDirName2[PATH_SIZE];
LPTSTR lpszDirName2;
HANDLE hFileRead;
HANDLE hFindFile;
WIN32_FIND_DATA FindFileData;
BOOL fNextFile;
DWORD dwLastError;
LONG lIndex, lCount;

if (!szDirName) {
MessageBox(hWnd, TEXT("DecryptDir: szDirName is NULL."), NULL, MB_OK);
return FALSE;
}

if (!lpCInfo) {
MessageBox(hWnd, TEXT("DecryptDir: lpCInfo is NULL."), NULL, MB_OK);
return FALSE;
}

GetDecryptedDirName(hWnd, szDirName, szDecryptedDirName, 1, &hFileRead);

// strip the brackets off szDecryptedDirName.

if (szDecryptedDirName[0] == TEXT('[')) {
for (i = 1, j = 0; szDecryptedDirName[i]
&& szDecryptedDirName[i] != TEXT(']'); i++, j++)
szBuf2[j] = szDecryptedDirName[i];
szBuf2[j] = TEXT('\0');
lstrcpy(szDecryptedDirName, szBuf2);
}

CloseHandle(hFileRead);

lpszDirName2 = szDirName2;

// Collapse the selected directory in the directory listbox.

lIndex = GetLBText(lpCInfo->hDirLB, Buf);
lCount = GetDirDepth(Buf, &lpszDirName2);
CollapseDir(lpCInfo, lIndex, lCount);

SeparatePathName2(szDirName, szDir, szFile);

// unhide the directory.

lstrcat(szDir, TEXT("\\"));
lstrcat(szDir, szDecryptedDirName);
SetFileAttributes(szDirName, GetFileAttributes(szDirName) ^ FILE_ATTRIBUTE_HIDDEN);

// rename the directory to its original name.

#ifdef UNICODE
if (_wrename((const wchar_t *)szDirName, (const wchar_t *)szDir)) {
#else
if (rename(szDirName, szDir)) {
#endif
MessageBox(hWnd, TEXT("Another process is using this directory."), NULL, MB_OK);
return FALSE;
}

lstrcpy(szDir2, szDir);
lstrcat(szDir2, TEXT("\\*"));

if ((hFindFile = FindFirstFile(szDir2, &FindFileData)) == INVALID_HANDLE_VALUE) {
MessageBox(hWnd, TEXT("DecryptDir: FindFirstFile failed."), NULL, MB_OK);
return FALSE;
}

// decrypt all the files in the directory, recursing on directories

do {
lstrcpy(szFileName, szDir);
lstrcat(szFileName, TEXT("\\"));
lstrcat(szFileName, FindFileData.cFileName);

// is szFileName encrypted?
if (IsEncrypted(szFileName)
   && lstrcmp(FindFileData.cFileName, TEXT("."))
   && lstrcmp(FindFileData.cFileName, TEXT(".."))) {
// is szFileName a directory?
if (CFilerIsDirectory(szFileName)) {
// Decrypt the directory szFileName.
if (!DecryptDir(hWnd, szFileName, lpCInfo)) {
MessageBox(hWnd, TEXT("DecryptDir: DecryptDir failed."), NULL, MB_OK);
return FALSE;
}
}
// decrypt the file szFileName.
else if (!CFILERDecryptFile(hWnd, szFileName)) {
MessageBox(hWnd, TEXT("DecryptDir: CFILERDecryptFile failed."), NULL, MB_OK);
return FALSE;
}
}

fNextFile = FindNextFile(hFindFile, &FindFileData);

dwLastError = GetLastError();

} while (fNextFile || dwLastError != ERROR_NO_MORE_FILES);

FindClose(hFindFile);

return TRUE;
}

/************************************************************************\
* GetSigDir()
*
* in parameters
* szSigDir - buffer to hold result
*
* purpose:
* stores in buffer szSigDir the name of the directory containing
* the signatures
*
* returns
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL GetSigDir(LPTSTR szSigDir)
{
TCHAR szSystemDirectory[PATH_SIZE];

if (!szSigDir) {
ErrorMsg(TEXT("GetSigDir: szSigDir is NULL."));
return FALSE;
}

// [system directory drive]:\sig -> szSigDir

GetSystemDirectory(szSystemDirectory, PATH_SIZE * sizeof(*szSystemDirectory));
szSigDir[0] = szSystemDirectory[0];
szSigDir[1] = TEXT('\0');
lstrcat(szSigDir, TEXT(":\\sig"));

return TRUE;
}


CRP.H

// CRP.H

#ifndef CRYPTROUTINES_INCLUDED
#define CRYPTROUTINES_INCLUDED

WINAPI EnterPass(HWND, UINT, UINT, LPARAM);
BOOL GetPass(HWND);
LRESULT WINAPI CryptDlgProc(HWND, UINT, UINT, LPARAM);
LRESULT WINAPI EncryptDlgProc(HWND, UINT, UINT, LPARAM);
LRESULT WINAPI HashDlgProc(HWND, UINT, UINT, LPARAM);
BOOL Logon(HWND);
BOOL Logoff(HWND);
BOOL CFILEREncryptFile(HWND, LPTSTR);
BOOL CFILERDecryptFile(HWND, LPTSTR);
BOOL GetEncryptPublicKey(HWND, HCRYPTKEY *);
BOOL GetSignaturePublicKey(HWND, HCRYPTKEY *);
HCRYPTKEY GetfnKey(HWND);
BOOL SignFile(HWND, LPTSTR);
BOOL VerifyFile(HWND, LPTSTR);
BOOL MakeHashDirectory(VOID);
BOOL SHAtoRadix64(LPBYTE, LPTSTR);
VOID Exit1(HWND, LPTSTR, HANDLE, HANDLE);
VOID Exit2(HWND, LPTSTR, HWND, HANDLE, HANDLE);
BOOL c2r64(INT, LPTSTR);
BOOL EncryptDir(HWND, LPTSTR, LPCINFO lpCInfo);
BOOL DecryptDir(HWND, LPTSTR, LPCINFO lpCInfo);
BOOLGenRandomName(LPTSTR);
BOOLGenRandomName2(LPTSTR);
BOOLHasSignature(LPTSTR, TABLE, BOOL *);
BOOL GetDecryptedFileName(HWND, LPTSTR, LPTSTR, HANDLE *);
BOOL GetDecryptedDirName(HWND, LPTSTR, LPTSTR, BOOL, HANDLE *);
BOOL HideSignatures(VOID);
BOOL ShowSignatures(VOID);
BOOL GetSigFileName(LPTSTR, LPTSTR);
BOOL DeleteHashDirectory(VOID);
BOOL GetSigDir(LPTSTR);

#endif


DRVPROC.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// DRVPROC.C

#include "cfiler.h"

extern HANDLE   ghModule;
extern HANDLEghwndMain;
extern HFONT    ghFont;
extern HANDLE   ghDrvThread;
extern HANDLE   ghMenu;
extern HWND     ghwndDrives;
extern HWND   ghwndDrv;
extern HWND     ghFocusWnd;
extern LPDINFO  glpDrives;
extern CRITICAL_SECTION    gDrvCS;

/**********************************************************************\
* DrawItem()
*
* in parmeters
* lpdis - Information the owner window must have to determine how
* to paint the owner-draw listbox
*
* Calls DrawText to output a string with the right color in the
* rectangle contained in lpdis
\**********************************************************************/

VOID APIENTRY DrawItem(LPDRAWITEMSTRUCT lpdis) {
COLORREF crOldColor;
TCHARszFile[256];
INT i, nLen;
TCHAR szBuf[275];
TEXTMETRIC tm;
RECT rc;

if (!lpdis) {
ErrorMsg(TEXT("DrawEntireItem: lpdis is NULL."));
return;
}

GetTextMetrics(lpdis->hDC, &tm);

  // Filenames such as Q&A.DOC should look correct.

  ReplaceEscapeCharacters((LPTSTR)lpdis->itemData, szBuf);

nLen = lstrlen(szBuf);

if (nLen > HEADER_SIZE) {
ErrorMsg(TEXT("fnKey has been Destroyed!"));
crOldColor = SetTextColor(lpdis->hDC, RGB(140, 140, 140));
CopyRect(&rc, &lpdis->rcItem);
       rc.left += 1;
       lstrcpy(szFile, TEXT("RecoverMe"));
       DrawText(lpdis->hDC, szFile, lstrlen(szFile), (LPRECT)&rc, DT_LEFT);
          SetTextColor(lpdis->hDC, crOldColor);
return;
}

if (szBuf[0] == TEXT('|')) {

// This is an encrypted file. Select gray as the color.

crOldColor = SetTextColor(lpdis->hDC, RGB(140, 140, 140));

for (i = 0; i < nLen - 1; i++)
szFile[i] = szBuf[i + 1];

szFile[i] = TEXT('\0');
}
else if (szBuf[0] == TEXT('>')) {

// This is an encrypted and signed file.

crOldColor = SetTextColor(lpdis->hDC, RGB(255, 140, 140));

for (i = 0; i < nLen - 1; i++)
szFile[i] = szBuf[i + 1];

szFile[i] = TEXT('\0');
}
else if (szBuf[0] == TEXT(';')) {

// This is a signed only file.

crOldColor = SetTextColor(lpdis->hDC, RGB(255, 0, 0));

for (i = 0; i < nLen - 1; i++)
szFile[i] = szBuf[i + 1];

szFile[i] = TEXT('\0');
}
else
lstrcpy(szFile, szBuf);

       CopyRect(&rc, &lpdis->rcItem);

       rc.left += 1;

       DrawText(lpdis->hDC, szFile, lstrlen(szFile), (LPRECT)&rc, DT_LEFT);

if (szBuf[0] == TEXT('|') || szBuf[0] == TEXT('>') || szBuf[0] == TEXT(';'))
       SetTextColor(lpdis->hDC, crOldColor);

       return;
}

/**********************************************************************\
* HandleSelectionState()
*
* in parmeters
* lpdis - Information the owner window must have to determine how
* to paint the owner-draw listbox
*
* purpose:
* Handles a change in an item selection state. This function must
* check the current background color for the rectangle contained
* in the DRAWITEMSTRUCT pointer, lpdis. The background color and
* text color must be changed appropriately.
\**********************************************************************/

VOID APIENTRY HandleSelectionState(LPDRAWITEMSTRUCT lpdis)
{
     if (!lpdis) {
     ErrorMsg(TEXT("HandleSelectionState: lpdis is NULL."));
     return;
     }

     if (lpdis->itemState & ODS_SELECTED) {
 if (GetBkColor(lpdis->hDC) == GetSysColor(COLOR_HIGHLIGHT)) {
return;
}
else {
SetTextColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
SetBkColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT));
DrawItem(lpdis);
return;
}
}
else {
if (GetBkColor(lpdis->hDC) != GetSysColor(COLOR_WINDOW)) {
return;
}
else {
SetTextColor(lpdis->hDC, GetSysColor(COLOR_WINDOWTEXT));
SetBkColor(lpdis->hDC, GetSysColor(COLOR_WINDOW));
DrawItem(lpdis);
return;
}
}
}

/**********************************************************************\
* DrawEntireItem()
*
* in parmeters
* lpdis - Information the owner window must have to determine how
* to paint the owner-draw listbox
*
* Sets the background color and the text color and calls DrawItem()
\**********************************************************************/

VOID APIENTRY DrawEntireItem(LPDRAWITEMSTRUCT lpdis)
{
        if (!lpdis) {
        ErrorMsg(TEXT("DrawEntireItem: lpdis is NULL."));
        return;
        }

        SetBkColor(lpdis->hDC, GetSysColor(COLOR_WINDOW));
        SetTextColor(lpdis->hDC, GetSysColor(COLOR_WINDOWTEXT));

        DrawItem(lpdis);

    __try {
        if (!lpdis) {
        ErrorMsg(TEXT("DrawEntireItem: lpdis is NULL."));
        return;
        }

        if (!lpdis->itemData) {
        ErrorMsg(TEXT("DrawEntireItem: lpdis->itemData is NULL."));
        return;
        }
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
        ErrorMsg(TEXT("DrawEntireItem: lpdis is BOGUS."));
        return;
        }

        /* Draw or erase appropriate frames */
        __try {
        HandleSelectionState(lpdis);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
ErrorMsg(TEXT("DrawEntireItem: Attempt to call HandleSelectionState raised an exception."));
return;
}
}

LRESULT  WINAPI DrvWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    LPDRAWITEMSTRUCT lpdis;
    LPMEASUREITEMSTRUCT lpmis;
    LPCOMPAREITEMSTRUCT lpcis;

    DWORD dwDirStyle = WS_BORDER | WS_CHILD | WS_VISIBLE |
                              LBS_NOINTEGRALHEIGHT | LBS_NOTIFY |
                              LBS_HASSTRINGS | LBS_WANTKEYBOARDINPUT |
                              LBS_DISABLENOSCROLL | WS_HSCROLL |
                              WS_VSCROLL |LBS_USETABSTOPS;

    DWORD dwFileStyle = WS_BORDER | WS_CHILD | WS_VISIBLE |
                              LBS_NOINTEGRALHEIGHT | LBS_NOTIFY |
                             LBS_OWNERDRAWFIXED | LBS_WANTKEYBOARDINPUT |
                              LBS_DISABLENOSCROLL | WS_HSCROLL |
                              LBS_EXTENDEDSEL | LBS_MULTIPLESEL |
                              LBS_MULTICOLUMN | LBS_SORT;

    switch (message){

        //
        // Creates the text and listbox windows for this Drv child and
        //  saves its handle in the per Drv child DRVCHILDINFO data structure.
        //
        case WM_CREATE: {
            LPCINFO lpCInfo;

            DWORD   dwLoop;

            LPDINFO lpWalk;

            LONG    lTabs = LISTBOX_TAB_SIZE;

            //
            // Initialize DRVCHILDINFO structure
            //
            lpCInfo = (LPCINFO) ((LPCREATESTRUCT) lParam)->lpCreateParams;

            if (!lpCInfo) {
            ErrorMsg(TEXT("DrvWndProc: lpCInfo is NULL."));
            return 0;
         }

         lpCInfo->hwnd = hwnd;

            // Create text window
            lpCInfo->hTextWnd = CreateWindow(TEXT("TextClass"), NULL,
                                   SS_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER,
                                    0, 0, 0, 0,
                                    lpCInfo->hwnd,
                                    (HMENU) TEXT_WINDOW_ID,
                                    ghModule,
                                    NULL);

            if (!lpCInfo->hTextWnd) {
            ErrorMsg(TEXT("DrvWndProc: WM_CREATE: CreateWindow failed."));
            return 0;
            }

            // Create Directory and File List boxes
            lpCInfo->hDirLB = CreateWindow(TEXT("LISTBOX"), NULL,
                                    dwDirStyle,
                                    0, 0, 0, 0,
                                    lpCInfo->hwnd,
                                    (HMENU) LISTDIR_ID,
                                    ghModule,
                                    NULL);

            if (!lpCInfo->hDirLB) {
            ErrorMsg(TEXT("DrvWndProc: WM_CREATE: CreateWindow failed."));
            return 0;
            }

            // Create directory listbox string table.

            lpCInfo->DirTable = TableNew();

if (!lpCInfo->DirTable) {
ErrorMsg(TEXT("WM_CREATE: TableNew failed."));
return 0;
}

            lpCInfo->hFileLB = CreateWindow(TEXT("LISTBOX"), NULL,
                                    dwFileStyle,
                                    0, 0, 0, 0,
                                    lpCInfo->hwnd,
                                    (HMENU) LISTFILE_ID,
                                    ghModule,
                                    NULL);

            if (!lpCInfo->hFileLB) {
            ErrorMsg(TEXT("DrvWndProc: WM_CREATE: CreateWindow failed."));
            return 0;
            }

            lpCInfo->FileTable = TableNew();

            if (!lpCInfo->FileTable) {
            ErrorMsg(TEXT("WM_CREATE: TableNew failed."));
            return 0;
            }

        if (!Logon(hwnd)) {
MessageBox(hwnd, TEXT("DrvWndProc: WM_CREATE: Logon failed. Terminating application."), NULL, MB_OK);
exit(1);
return 0;
}

//
            // fDirLeft indicates whether the Directory ListBox defaults to
            //  the left side of each of the two drive windows.
            // fDirExpand indicates whether the Directory Listbox defaults
            //  to full expansion.
            //
            lpCInfo->fDirLeft = TRUE;
            lpCInfo->fDirExpand = FALSE;
            lpCInfo->fSuicide = FALSE;

            //
            // Create Mutex associated with each list box
            //
            lpCInfo->hDirMutex = CreateMutex(NULL, FALSE, NULL);
            lpCInfo->hFileMutex = CreateMutex(NULL, FALSE, NULL);

            //
            // Associate window with the current directory LPDINFO structure
            //   from the Drives linked list
            //

            dwLoop = GetCurrentDirectory( DIRECTORY_STRING_SIZE,
                                          lpCInfo->CaptionBarText);

            CharUpper(lpCInfo->CaptionBarText);

            WaitForSingleObject(ghDrvThread, INFINITE);
            EnterCriticalSection(&gDrvCS);

            lpWalk = glpDrives;

            if(dwLoop && dwLoop <= DIRECTORY_STRING_SIZE) {
            while(lpWalk && tolower(lpWalk->DriveName[0])
            != tolower((lpCInfo->CaptionBarText)[0]))
                    lpWalk = lpWalk->next;

                if( !lpWalk ){
                    ErrorMsg(TEXT("Drive Child Create: Drive list failure."));
                    LeaveCriticalSection(&gDrvCS);
                    return(-1);
                }
            }
            else{
                ErrorMsg(TEXT("Drive Child Create: GetCurrentDir failure."));
                LeaveCriticalSection(&gDrvCS);
                return(-1);
            }

            LeaveCriticalSection(&gDrvCS);

            lpCInfo->lpDriveInfo = lpWalk;

            //
            // Save the handle to DRVCHILDINFO in our window structure
            //
            SetWindowLong(hwnd, GWL_USERDATA, (LONG) lpCInfo);

            //
            // Initialize child windows
            //
            if( !SendMessage(lpCInfo->hDirLB, LB_SETTABSTOPS, (WPARAM)1,
                            (LPARAM)&lTabs) )
                ErrorMsg(TEXT("Drv window Create: Set tab stop failure."));

            //
            // Set default font.
            //
            SendMessage(lpCInfo->hDirLB, WM_SETFONT, (WPARAM)ghFont, (LPARAM)FALSE);
            SendMessage(lpCInfo->hFileLB, WM_SETFONT, (WPARAM)ghFont, (LPARAM)FALSE);

            SendMessage(hwnd, WM_COMMAND, (WPARAM)MM_REFRESH, (LPARAM)NULL);

            return 1;
        }

        case WM_COMMAND: {
          static LPCINFO     lpCInfo;
          static SELECTINFO  Select;

          //
          // Retrieving this child window's DRVCHILDINFO data for displaying
          //    messages in the text window
          //
          lpCInfo = (LPCINFO) GetWindowLong(hwnd, GWL_USERDATA);

          if (!lpCInfo) {
          ErrorMsg(TEXT("DrvWndProc: WM_COMMAND: lpCInfo is NULL."));
          return FALSE;
          }

          switch (LOWORD(wParam)){

            //
            //  Clears the selection in the active window.
            //  Sent when user hits escape key.
            //
            case MM_ESCAPE:{
                //
                // If there is a directory expand in process, kill the
                //  thread, and leave the listbox in a semi-expanded state.
                //  Else, clear file selection, and switch to command window.
                //
                if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
                        == WAIT_TIMEOUT ){
                    lpCInfo->fSuicide = TRUE;
                    lpCInfo->fEscape = TRUE;
                }
                else
                    ReleaseMutex( lpCInfo->hDirMutex );

                SendMessage(lpCInfo->hFileLB, LB_SETCURSEL, (WPARAM)-1,
                               (LPARAM)0);

                return 1;
            }

            case MM_ENCRYPT_DECRYPT: {
                if( ghFocusWnd == lpCInfo->hFileLB ) {
    RunListBoxItem(lpCInfo, ENCRYPT_DECRYPT);
                }
                else
                    if( ghFocusWnd == lpCInfo->hDirLB ){
                        if( !PostMessage(hwnd, WM_COMMAND, MM_FILLDIR,
                                         (LPARAM)0) ){
                            ErrorMsg(TEXT("MM_ENCRYPT_DECRYPT: Filldir failure."));
                            return 0;
                        }
                    }

                    return 1;
            }

case MM_SIGN: {
if( ghFocusWnd == lpCInfo->hFileLB ) {
    RunListBoxItem(lpCInfo, SIGN);
                }
                else
                    if( ghFocusWnd == lpCInfo->hDirLB ){
                        if( !PostMessage(hwnd, WM_COMMAND, MM_FILLDIR,
                                         (LPARAM)0) ){
                            ErrorMsg(TEXT("MM_SIGN: Filldir failure."));
                            return 0;
                        }
                    }

                return 1;
            }

case MM_VERIFY: {
if( ghFocusWnd == lpCInfo->hFileLB ) {
    RunListBoxItem(lpCInfo, VERIFY);
                }
                else
                    if( ghFocusWnd == lpCInfo->hDirLB ){
                        if( !PostMessage(hwnd, WM_COMMAND, MM_FILLDIR,
                                         (LPARAM)0) ){
                            ErrorMsg(TEXT("MM_VERIFY: Filldir failure."));
                            return 0;
                        }
                    }

                return 1;
            }

            case MM_EXPAND:{

                lpCInfo->fDirExpand = !lpCInfo->fDirExpand;

                if( lpCInfo->fDirExpand )
                    CheckMenuItem( ghMenu, MM_EXPAND,
                                    MF_BYCOMMAND | MF_CHECKED);
                else
                    CheckMenuItem( ghMenu, MM_EXPAND,
                                MF_BYCOMMAND | MF_UNCHECKED);

                if( !SendMessage( (HWND)lpCInfo->hwnd, WM_COMMAND,
                                 (WPARAM)MM_REFRESH, (LPARAM)0 ) ){
                    ErrorMsg(TEXT("MM_EXPAND:  MM_REFRESH failure."));
                    return 0;
                }
                return 1;
            }

            //
            // refreshes contents of directory and file ListBoxes.
            //
            case MM_REFRESH:{

                DWORD   dwThreadID;
                INT i, N;

                if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
                        == WAIT_TIMEOUT )
                    //
                    // If the full directory expand has been cancled, kill the
                    //  existing thread.
                    //
                    if( !lpCInfo->fDirExpand && !lpCInfo->fSuicide){
                        lpCInfo->fSuicide = TRUE;
                        return 1;
                    }
                    else{
                        return 0;
                    }

                // if set, clear the expand dir. user abort (escape key) flag.
                if( lpCInfo->fEscape ){
                    lpCInfo->fEscape = FALSE;
                    ReleaseMutex( lpCInfo->hDirMutex );
                    return 1;
                }

                // At this point, the Dir LB mutex has been grabbed.

                // Clear directory LB.  If expand flag is set, expand all
                //  directories.  Refill File LB.
                //

                N = GetSize(lpCInfo->DirTable);

   for (i = 0; i < N; i++)
   TableRemove(lpCInfo->DirTable, 0);

   if( SendMessage( lpCInfo->hDirLB, LB_RESETCONTENT,
                                 (WPARAM)0, (LPARAM)0 ) < 0 ){
                    ErrorMsg(TEXT("Refresh Drv window: Reset Dir LB content failure."));
                    ReleaseMutex( lpCInfo->hDirMutex );
                    return 0;
                }

     //
                // This call puts the default root entry back into the empty
                //  LB.  Set suicide flag to false to ensure it will complete.
                //
                lpCInfo->fSuicide = FALSE;
                ExpDir( lpCInfo );

                //
                // All the Dir LB work is done.  Release Dir LB Mutex.
                //
                ReleaseMutex( lpCInfo->hDirMutex );

                if( lpCInfo->fDirExpand ){

                    CloseHandle( lpCInfo->hDirThread );

                    lpCInfo->hDirThread = CreateThread( NULL, 0,
                                  (LPTHREAD_START_ROUTINE)FullExpand,
                                  (LPVOID)lpCInfo, 0, &dwThreadID);

                    if( !lpCInfo->hDirThread ){
                        ErrorMsg(TEXT("MM_REFRESH: FullExpand CreateThread failure."));
                        return 0;
                    }
                }
                else
                    ExpDir( lpCInfo );

                if( !PostMessage(hwnd, WM_COMMAND, MM_FILLFILE,
                                 (LPARAM)0) ){
                    ErrorMsg(TEXT("Refresh Drv window: Fillfile failure."));
                    return 0;
                }

                return 1;
            }

            //
            //  Fill listbox in lParam with directory from Drv child's drive.
            //  Sent by MM_REFRESH.
            //
            //  lParam == 0
            //
            case MM_FILLDIR:{

                DWORD   dwThreadID;

                lpCInfo->fSuicide = FALSE;

                CloseHandle( lpCInfo->hDirThread );

                lpCInfo->hDirThread = CreateThread( NULL, 0,
                              (LPTHREAD_START_ROUTINE)ExpDir,
                              (LPVOID)lpCInfo, 0, &dwThreadID);

                if( !(lpCInfo->hDirThread) ){
                    ErrorMsg(TEXT("MM_FILLDIR: ExpDir CreateThread failure."));
                    return 0;
                }

                return 1;
            }

            //
            //  Fill listbox in lParam with files from current directory.
            //  Sent by MM_REFRESH & LBN_DBLCLK in DrvWndProc.
            //
            //  lParam == 0
            //
            case MM_FILLFILE:
                ghFocusWnd = lpCInfo->hFileLB;
                return FillFile(lpCInfo, hwnd);
break;

            //
            // The following WM_COMMAND messages are sent by the listboxes
            //
            // HIWORD(wParam) = LB notification message
            // lParam = LB window handle
            //
            case LISTFILE_ID:{
              switch( HIWORD(wParam) ){
                //
                // In case of double click on a directory, expand the file
                // Listbox. if on a file name, run or edit file.
                //
                case LBN_DBLCLK:{
                    RunListBoxItem(lpCInfo, ENCRYPT_DECRYPT);
                    return 1;
                }
                break;

                case LBN_SETFOCUS:{
                    ghFocusWnd = lpCInfo->hFileLB;
                }
                break;

                default:
                    return 1;
              }
            } // LISTFILE_ID
            break;

            //
            // Notification from the Directory ListBox
            //
            case LISTDIR_ID:{
              switch( HIWORD(wParam) ){

                case LBN_SETFOCUS:{
                    ghFocusWnd = lpCInfo->hDirLB;
                }
                break;

                //
                // Expand subdirectories in dir listbox
                //
                case LBN_DBLCLK:{

                    if( !PostMessage(hwnd, WM_COMMAND, MM_FILLDIR,
                                     (LPARAM)0) ){
                        ErrorMsg(TEXT("Dir ListBox Notify: Filldir failure."));
                        return 0;
                    }
                    return 1;
                }
                break;

                case LBN_SELCHANGE:{
                    //
                    // for the Directory LB, fill the
                    // corresp. File LB with items in the newly selected dir.
                    //
                    LONG lIndex;

                    if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
                            == WAIT_TIMEOUT ){
                        ErrorMsg(TEXT("Dir LB Notify: Dir LB Mutex Timeout."));
                        return 0;
                    }

                    //
                    // Retrieve selected (careted) item.
                    //
                    lIndex = SendMessage( (HWND)lParam, LB_GETCARETINDEX,
                                        (WPARAM)NULL, (LPARAM)NULL );

                    if( !ConstructDirName(lpCInfo, lIndex,
                                          lpCInfo->CaptionBarText) ){
                        ErrorMsg(TEXT("Dir LB Notify:  ConstructDirName failure."));
                        ReleaseMutex( lpCInfo->hDirMutex );
                        return 0;
                    }

                    ReleaseMutex( lpCInfo->hDirMutex );

                    if( !PostMessage(hwnd, WM_COMMAND, MM_FILLFILE,
                                     (LPARAM)0) ){
                        ErrorMsg(TEXT("Dir ListBox Notify: Fillfile failure."));
                        return 0;
                    }
                } // LBN_SELCHANGE
                break;

                default:
                    return 1;
              }
            } //  LISTDIR_ID
            break;

            default:
               return 1;
          }
        }
        break;

        //
        // Whenever the Drv child window is resized, its children has to be
        //  resized accordingly.  The GetWindowLong GWL_USERDATA values
        //  contain the height of the windows queried, set in their respective
        //  WM_CREATE cases.
        //
        case WM_SIZE: {
            LPCINFO     lpCInfo;

            int         nListHeight,
                        nListWidth;

            HWND        hLeftLB,
                        hRightLB;

            //
            // First, get the text window's handle from the per Drv child
            //  DRVCHILDINFO data structure
            //
            lpCInfo = (LPCINFO)GetWindowLong(hwnd, GWL_USERDATA);

            if (!lpCInfo) {
            ErrorMsg(TEXT("DrvWndProc: WM_SIZE: lpCInfo is NULL."));
            return 0;
            }

            nListHeight = HIWORD(lParam) -
                          GetWindowLong(lpCInfo->hTextWnd, GWL_USERDATA)
                          - LIST_BORDER * 2;

            nListWidth = (LOWORD(lParam) - LIST_BORDER) / 2 - LIST_BORDER;

            //
            // Always, put the text window at the top of the Drv window.
            // Increasing sides and bottom extents by 1 to overlap border
            //   with Drv child border
            //
            MoveWindow(lpCInfo->hTextWnd,
                       -1,
                       0,
                       LOWORD(lParam) + 2,
                       GetWindowLong(lpCInfo->hTextWnd, GWL_USERDATA) + 1,
                       TRUE);

            if( lpCInfo->fDirLeft ){
                hLeftLB = lpCInfo->hDirLB;
                hRightLB = lpCInfo->hFileLB;
            }
            else{
                hLeftLB = lpCInfo->hFileLB;
                hRightLB = lpCInfo->hDirLB;
            }

            MoveWindow(hLeftLB,
                       LIST_BORDER,
                       GetWindowLong(lpCInfo->hTextWnd, GWL_USERDATA) + 1
                         + LIST_BORDER,
                       nListWidth,
                       nListHeight,
                       TRUE);

            MoveWindow(hRightLB,
                       (LOWORD(lParam) + LIST_BORDER) / 2,
                       GetWindowLong(lpCInfo->hTextWnd, GWL_USERDATA) + 1
                         + LIST_BORDER,
                       nListWidth,
                       nListHeight,
                       TRUE);

        break;
        }

        case WM_PARENTNOTIFY:{
            LPCINFO lpCInfo;

            if(wParam == WM_LBUTTONDOWN){
                lpCInfo = (LPCINFO) GetWindowLong(hwnd, GWL_USERDATA);
                if(lpCInfo == NULL){
                    ErrorMsg(TEXT("Drv child: Parent notify failure."));
                    return 1;
                }
                if(HIWORD(wParam) == LISTDIR_ID)
                    SetFocus(lpCInfo->hDirLB);
                else
                    if(HIWORD(wParam) == LISTFILE_ID)
                        SetFocus(lpCInfo->hFileLB);
                    else
                        if(HIWORD(wParam) == TEXT_WINDOW_ID)
                            SetFocus(lpCInfo->hTextWnd);
            }

            break;
        }

        //
        // Same as MainWndProc's MM_ACTIVEDRV case.  The initial PostMessage
        //   is so the currently active Drv child will not process the message
        //   until it is no longer in focus.
        //
        case WM_MOUSEACTIVATE:{
            LPCINFO lpCInfo;

            PostMessage(ghwndDrv, WM_COMMAND, (WPARAM)MM_TOGGLE,
                        (LPARAM)NULL);
            ghwndDrv = hwnd;
            SendMessage(ghwndDrv, WM_COMMAND, (WPARAM)MM_TOGGLE,
                        (LPARAM)NULL);

            lpCInfo = (LPCINFO) GetWindowLong(hwnd, GWL_USERDATA);

            if (!lpCInfo) {
            ErrorMsg(TEXT("DrvWndProc: lpCInfo is NULL."));
            return FALSE;
            }

            SendMessage(ghwndDrives, WM_COMMAND, MM_ACTIVEDRV,
                        (LPARAM)lpCInfo->lpDriveInfo);

            break;
        }

        //
        // Free the DRVCHILDINFO data that associates with this window
        //  also, reset the menu.
        //
        case WM_CLOSE: {
            LPCINFO lpCInfo;

            lpCInfo = (LPCINFO)GetWindowLong(hwnd, GWL_USERDATA);

            if (!lpCInfo) {
            ErrorMsg(TEXT("DrvWndProc: WM_CLOSE: lpCInfo is NULL."));
            return 0;
            }

            CloseHandle(lpCInfo->hDirThread);
            CloseHandle(lpCInfo->hDirMutex );
            CloseHandle(lpCInfo->hFileMutex );
TableFree(lpCInfo->DirTable);
TableFree(lpCInfo->FileTable);

break;
        }

                //   case WM_DELETEITEM:
            case WM_DRAWITEM: {

            // Get pointer to the DRAWITEMSTRUCT
            lpdis = (LPDRAWITEMSTRUCT)lParam;

            switch (lpdis->itemAction)

{
            case ODA_DRAWENTIRE:
            DrawEntireItem(lpdis);
            break;

            case ODA_SELECT:
            HandleSelectionState(lpdis);
            break;

            }

              // Return TRUE meaning that we processed this message.
            return 1;
}

        case WM_MEASUREITEM:{

            lpmis = (LPMEASUREITEMSTRUCT)lParam;

            // All the items are the same height since the list box style is
            // LBS_OWNERDRAWFIXED
            //
            lpmis->itemHeight = 15;
            return 1;
}

            case WM_COMPAREITEM: {
INT cv;

lpcis = (LPCOMPAREITEMSTRUCT)lParam;

if (!lpcis->itemData1)
ErrorMsg(TEXT("WM_COMPAREITEM: lpcis->itemData1 is NULL."));

if (!lpcis->itemData2)
ErrorMsg(TEXT("WM_COMPAREITEM: lpcis->itemData2 is NULL."));

cv = CFilerlstrcmp((LPTSTR)(lpcis->itemData1), (LPTSTR)(lpcis->itemData2));

if (cv < 0)
return -1;
else if (cv == 0)
            return 0;
else if (cv > 0)
            return 1;

}

            default:
            return DefWindowProc(hwnd, message, wParam, lParam);

    } //switch
    return DefWindowProc(hwnd, message, wParam, lParam);
}

/*************************************************************************************\
* TextWndProc()
*
* Text Window procedure for displaying miscellaneous messages to user.
\*************************************************************************************/

LRESULT WINAPI TextWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    switch (message)
    {
    case WM_CREATE:
        {
        HDC        hDC;
        HGDIOBJ    hOldFont;
        TEXTMETRIC tm;
        LONG       lHeight;

        hDC = GetDC(hwnd);

        hOldFont = SelectObject(hDC, ghFont);
        GetTextMetrics(hDC, &tm);

        //
        // base the height of the window on size of text
        //
        lHeight = tm.tmHeight + GetSystemMetrics(SM_CYBORDER) + 6;

        //
        // saved the height for later reference
        //
        SetWindowLong(hwnd, GWL_USERDATA, lHeight);

            if(hOldFont)
                SelectObject(hDC, hOldFont);

            ReleaseDC(hwnd, hDC);
            break;
        }

    case WM_SETTEXT:
            DefWindowProc(hwnd, message, wParam, lParam);
            if( !InvalidateRect(hwnd, NULL, TRUE) ){
                ErrorMsg(TEXT("Textwindow: Set text failure."));
                return 1;
            }
            UpdateWindow(hwnd);
            return 1;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            RECT        rc;
            TCHAR       ach[BUF_SIZE];
            int         len, nxBorder, nyBorder;
            HFONT       hOldFont = NULL;
            HBRUSH      hBrush;

            BeginPaint(hwnd, &ps);

            GetClientRect(hwnd,&rc);

            len = GetWindowText(hwnd, ach, BUF_SIZE);

            SetBkMode(ps.hdc, TRANSPARENT);

            if( GetParent(hwnd) == ghwndDrv ){
                hBrush = CreateSolidBrush( GetSysColor(COLOR_ACTIVECAPTION) );
                SetTextColor( ps.hdc, GetSysColor(COLOR_CAPTIONTEXT) );
            }
            else{
                hBrush = CreateSolidBrush( GetSysColor(COLOR_INACTIVECAPTION) );
                SetTextColor( ps.hdc, GetSysColor(COLOR_INACTIVECAPTIONTEXT) );
            }

            hOldFont = SelectObject(ps.hdc, ghFont);

            FillRect(ps.hdc, &rc, hBrush);

            nxBorder = GetSystemMetrics(SM_CXBORDER);
            rc.left  += 9*nxBorder;
            rc.right -= 9*nxBorder;

            nyBorder = GetSystemMetrics(SM_CYBORDER);
            rc.top    += 3*nyBorder;
            rc.bottom -= 3*nyBorder;

            ExtTextOut(ps.hdc, rc.left+2*nxBorder, rc.top, ETO_CLIPPED,
                    &rc, ach, len, NULL);

            SetBkMode(ps.hdc, OPAQUE);

            if (hOldFont)
                SelectObject(ps.hdc, hOldFont);

            DeleteObject(hBrush);

            EndPaint(hwnd, &ps);
            return 1;
        }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}


DRVPROC.H

// DRVPROC.H

#ifndef DRVPROC_INCLUDED
#define DRVPROC_INCLUDED

#include <wincrypt.h>

BOOL    RunListBoxItem      (LPCINFO, INT);
BOOL    ExecuteFileAction   (LPSINFO);
BOOL    FillSelectDlg       (HWND, LPSINFO);
BOOL    DoFileIO            (HWND, LPSINFO);
VOID    UpdateFileLB        (HWND);
BOOL    HandleIOError       (HWND, DWORD, LPTSTR, LPTSTR);
LRESULT WINAPI DrvWndProc   (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI VersionProc  (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI SelectProc   (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI RenameProc   (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI MkDirProc    (HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI TextWndProc  (HWND, UINT, WPARAM, LPARAM);
VOID APIENTRY HandleSelectionState(LPDRAWITEMSTRUCT);
VOID APIENTRY DrawEntireItem(LPDRAWITEMSTRUCT);
VOID APIENTRY DrawItem(LPDRAWITEMSTRUCT);

#endif


ENUMDRV.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//    PROGRAM: ENUMDRV.C
//
//    PURPOSE: Determines all drives in the system, both local and remote,
//             and their file system type
//

#define  STRICT

#include "cfiler.h"

LPTSTR lpDriveStrings = NULL;
extern CRITICAL_SECTION gDrvCS;  // Drive list critical section var.
extern LPDINFO          glpDrives;

/********************************************************************************\
*    FUNCTION: CheckRM(LPTSTR)
*
*    PURPOSE: Verifies that a removeable media drive contains a disk
*
*    COMMENTS:
*
*        This function is called each time a drive type is determined to be
*        removeable (DRIVE_REMOVEABLE).  An attempt is made to open a
*        file in the root directory of the drive.  If the attempt succeeds,
*        then media is present in the drive and subsequent calls to the
*        drive can be safely made.  If the call fails, then there is no media
*        present in the drive and no attempts should be made to access this
*        drive.  The Error Mode is temporarily set to 1 to allow failures
*        to immediately return to the calling program.  This eliminates
*        unwanted dialog boxes that prompt for disks to be placed in the
*        drive.
*
*    INPUT: szDriveName - removeable media drive name (ex - "a:")
*
*    OUTPUT: Returns TRUE if media present
*                    FALSE if media is not present
\*********************************************************************************/

BOOL CheckRM( LPTSTR lpszDriveName )
{
    TCHAR    szFileName[DIRECTORY_STRING_SIZE];
    DWORD    dwHold;

    SetErrorMode( SEM_FAILCRITICALERRORS );

    lstrcpy( szFileName, lpszDriveName );
    lstrcat( szFileName, TEXT(".") );

    dwHold = GetFileAttributes( szFileName );

    SetErrorMode( 0 );

    //  If no error, media must be in drive.
    if( dwHold != 0xFFFFFFFF ){
        return(TRUE);
    }
    else{
        dwHold = GetLastError();
        if( dwHold != ERROR_NOT_READY )
            ErrorMsg(TEXT("CheckRM: Get Removable Media Info Failure."));

        return(FALSE);
    }

}

/*********************************************************************************\
*    FUNCTION: DWORD EnumDrives(*LPDINFO)
*
*    PURPOSE: Enumerates available drives, and information on them. If the
*               DINFO structure passed in is NULL, it creates a linked list
*               of DINFO structures, reporting information on each available
*              drive.  If DINFO points to an existing structure, the list
*              is updated.
*
*   COMMENTS:
*       The number of available drives is first determined by a call to
*       GetLogicalDrives.  This provides a bit mask of all logical drives.
*
*       For each logical drive, a call is made to GetDriveType to determine
*       the drive type.  If the logical drive is not in the bit mask that
*       was created with the GetLogicalDrives call, then this drive is
*       bypassed.
*
*   INPUT:  LPDINFO dINfo: A pointer to a DRVINFO Structure.
*
*   OUTPUT: Returns the number of DINFO structures in the linked
*           list pointed to by dInfo.  Value is negative if error.
\**********************************************************************************/

void EnumDrives(LPDINFO *lplpRoot)
{
  DWORD         dwDriveMask;
  DWORD         dwCount;
  LPTSTR        lpszRootPathName=TEXT("?:\\");

  LPTSTR        lpParse;

  LPDINFO       lpDInfo = (LPDINFO)0,                // new node ptr
                lpHold = (LPDINFO)0,                 // list walking ptrs
                lpBack = (LPDINFO)0,
                lpRoot = *lplpRoot;     // root ptr

  EnterCriticalSection(&gDrvCS);

  dwCount=GetLogicalDriveStrings( 0, lpDriveStrings);
  if( !dwCount ){
    LeaveCriticalSection(&gDrvCS);
    ErrorMsg(TEXT("EnumDrives: Get Drive Strings error"));
    ExitThread((DWORD)-1);
  }

  // allocate memory, +1 for trailing NULL
  lpDriveStrings = (LPTSTR)malloc((dwCount + 1) * sizeof(TCHAR));

  if( lpDriveStrings == NULL){
      LeaveCriticalSection(&gDrvCS);
      ErrorMsg(TEXT("EnumDrives: Allocation error"));
      ExitThread((DWORD)-2);
  }

  if(dwCount < GetLogicalDriveStrings( dwCount, lpDriveStrings) ){
      LeaveCriticalSection(&gDrvCS);
      ErrorMsg(TEXT("EnumDrives: Drive String size Changed!"));
      ExitThread((DWORD)-3);
  }
  lpParse = lpDriveStrings;

  dwDriveMask=GetLogicalDrives();

  //
  // walk list, inserting, deleting, & updating nodes as necessary
  //
  dwCount = 0;
  lpHold = lpBack = lpRoot;

  for (*lpszRootPathName=TEXT('a');*lpszRootPathName<=TEXT('z');(*lpszRootPathName)++){
    if (dwDriveMask & 1){   // drive exists. Insert or update.

      dwCount++;

      //
      // if lpHold (the list walking ptr) is NULL, or the drive that exists
      //  does not already have a node in the list, allocate a node.
      //
      if( !lpHold || lpHold->DriveLetter > *lpszRootPathName ){
        //
        // Allocating memory for DRVINFO node
        //
        lpDInfo = (LPDINFO) malloc((DWORD)sizeof(DRVINFO));

        if( lpDInfo == NULL){
          LeaveCriticalSection(&gDrvCS);
          ErrorMsg(TEXT("EnumDrives: DRVINFO Allocation error"));
          ExitThread((DWORD)-4);
        }

        //
        // insert new node into list
        //
        if( lpHold == lpRoot ){
          lpDInfo->next = lpRoot;
          lpRoot = lpDInfo;
        }
        else{
          lpDInfo->next = lpBack->next;
          lpBack->next = lpDInfo;
        }

        lpBack = lpDInfo;
        lpHold = lpDInfo->next;

        lpDInfo->DriveLetter = *lpszRootPathName;
      }
      else{
        if( lpBack != lpHold )
          lpBack = lpBack->next;
        lpHold = lpHold->next;
      }

      lpBack->DriveType = GetDriveType(lpszRootPathName);

      lpBack->DriveName = lpParse;
      lpParse += lstrlen(lpParse)+1;
    }
    else{               // drive does not exist.
      if( lpHold )      //   if node exists, delete it.
        if( lpHold->DriveLetter == *lpszRootPathName ){
          if( lpHold == lpRoot )
            lpRoot = lpBack = lpHold->next;
          else
            lpBack->next = lpHold->next;

          free((LPVOID)lpHold);

          lpHold = lpBack->next;
        }
    }
    dwDriveMask >>= 1;

  } // end for

  *lplpRoot = lpRoot;

  LeaveCriticalSection(&gDrvCS);

  ExitThread(dwCount);
}


ENUMDRV.H

// ENUMDRV.H

#ifndef ENUMDRV_INCLUDED
#define ENUMDRV_INCLUDED

// procedure definitions

BOOL   CheckRM      (LPTSTR);
BOOL   FillRDStruct (LPDINFO);
VOID   EnumDrives   (LPDINFO*);

#endif


EXPDIR.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//    PROGRAM: EXPDIR.C
//
//    PURPOSE: Directory Listbox expansion/collapse functions

#define  STRICT

#include "cfiler.h"

extern HANDLE ghModule;

/**********************************************************************************\
* ExpDir()
*
* Thread Function
* Called when directory is selected (Enter or Double-click).  Expands subdirs
*  of selected directory if not expanded, indenting subdirs.  If
*  already expanded, deletes all subdirs from listbox
*
* Returns:  TRUE if successful, FALSE if error.
\***********************************************************************************/

BOOL ExpDir(LPCINFO lpCInfo)
{
    HANDLE          hFile = NULL;   //  Find file handle
    WIN32_FIND_DATA FileData;       //  Find file info structure

    TCHAR           szFileName[BUF_SIZE];
TCHARszFileName2[BUF_SIZE];
TCHARszInsertString[DIRECTORY_STRING_SIZE << 1];
TCHARszBuf[BUF_SIZE];

    PTCHAR          lpszHold;      //  Points to actual name in LB string

    BOOL            fDone = FALSE;  //  Loop flag for finding files in dir

    DWORD           dwAttrib;       //  Holds file attribute flags

    LONG            lIndex,         //  Index of selected listbox member
                    lDirDepth;

    INT i;

   if (!lpCInfo) {
   ErrorMsg(TEXT("ExpDir: lpCInfo is NULL."));
   return FALSE;
   }

   for (i = 0; i < BUF_SIZE; i++) {
szFileName[i] = TEXT('\0');
szFileName2[i] = TEXT('\0');
}

    if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
            == WAIT_TIMEOUT ){
        ErrorMsg(TEXT("ExpDir: Dir LB Mutex Timeout."));
        return(0);
    }

    //
    // If the LB is empty, we insert the root, unexpanded
    //
    if( !SendMessage( lpCInfo->hDirLB, LB_GETCOUNT, 0, 0) ){

        TCHAR   szHold[DIRECTORY_STRING_SIZE << 1];

        szHold[0] = TEXT('+');
        lstrcpy( &(szHold[1]), lpCInfo->lpDriveInfo->DriveName );

        //
        // Also copy the DriveName to the Caption Bar.
        //
        lstrcpy(lpCInfo->CaptionBarText, lpCInfo->lpDriveInfo->DriveName );

        SendMessage(lpCInfo->hDirLB, LB_ADDSTRING, 0,
                    (LPARAM)szHold );

        if (!TableAdd(lpCInfo->DirTable, lpCInfo->lpDriveInfo->DriveName, szHold)) {
ErrorMsg(TEXT("ExpDir: TableAdd failed."));
return FALSE;
}

        ReleaseMutex( lpCInfo->hDirMutex );
        return(1);
    }

    //
    // Retrieve index of selected (careted) directory.
    //
    lIndex = SendMessage( lpCInfo->hDirLB, LB_GETCARETINDEX,
                          (WPARAM)NULL, (LPARAM)NULL );

    if( SendMessage( lpCInfo->hDirLB, LB_GETTEXT, (WPARAM)lIndex,
                        (LPARAM)szFileName ) < 0 ){
        ErrorMsg(TEXT("Expand Directory:  Get listbox text failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    lDirDepth = GetDirDepth( szFileName, &lpszHold );

    if( lDirDepth == -1 ){
        ErrorMsg(TEXT(" Expand Directory:  GetDirDepth failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    //
    // If Directory is already expanded, collapse it, and vice versa.
    //  First, change the symbol.
    //
    if( *lpszHold == TEXT('-'))
        *lpszHold = TEXT('+');
    else
        *lpszHold = TEXT('-');

    //
    // Clear WM_SETREDRAW flag, so changes will not be seen until entire
    //  expansion/collapse is complete.  Reset flag at end of function
    //
    if( SendMessage(lpCInfo->hDirLB, WM_SETREDRAW, (WPARAM)FALSE,
                (LPARAM)0 ) < 0){
        ErrorMsg(TEXT("Expand Directory:  Clear redraw flag failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    //
    // Delete old dir string, insert new, and reset the selection
    //
    if( SendMessage(lpCInfo->hDirLB, LB_DELETESTRING, (WPARAM)lIndex,
                (LPARAM)0 ) < 0){
        ErrorMsg(TEXT("Expand Directory:  Delete dir string failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }
else {
if (!TableGetHidden(lpCInfo->DirTable, lIndex, szBuf)) {
ErrorMsg(TEXT("ExpDir: TableGetHidden failed."));
return 0;
}
lstrcpy(szInsertString, szBuf);
TableRemove(lpCInfo->DirTable, lIndex);
}

    if( SendMessage(lpCInfo->hDirLB, LB_INSERTSTRING, (WPARAM)lIndex,
               (LPARAM)szFileName ) < 0){
        ErrorMsg(TEXT("Expand Directory:  Insert dir string failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }
else {
if (!TableInsert(lpCInfo->DirTable, szInsertString, szFileName, lIndex)) {
ErrorMsg(TEXT("ExpDir: TableInsert failed."));
return 0;
}
}

    if( SendMessage(lpCInfo->hDirLB, LB_SETCURSEL, (WPARAM)lIndex,
               (LPARAM)0 ) < 0){
        ErrorMsg(TEXT("Expand Directory:  Insert dir string failure"));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    //
    // The symbol has been changed, now collapse if needed, then reset
    //  redraw flag, leave critical section, and exit.
    //
    if( *lpszHold == TEXT('+') ){
        CollapseDir( lpCInfo, lIndex, lDirDepth);
        ExpDirExit(lpCInfo, hFile);
        return(1);
    }

    //
    // If we're here, directory needs to be expanded.
    //  Enumerate subdirectories beneath the dir entry in the listbox.
    //
    // First, Get the full path of the directory (in szFileName)
    //
    if (!TableGetHidden(lpCInfo->DirTable, lIndex, szBuf)) {
    ErrorMsg(TEXT("ExpDir: TableGetHidden failed."));
    return FALSE;
    }

    lstrcpy(szFileName, szBuf);

    //
    // Check to see if there is a terminating backslash
    //  Then append a '*' as a wildcard for FindFirstFile.
    //
    lpszHold = &szFileName[lstrlen(szFileName)];

    lpszHold--;
    if( *lpszHold != TEXT('\\') ){
        lpszHold++;
        *lpszHold = TEXT('\\');
    }

    lstrcpy(szFileName2, szFileName);

    lpszHold++;
    lstrcpy( lpszHold, TEXT("*"));

    //
    // Start a search on all the files within the directory
    //
    hFile = FindFirstFile( szFileName, &FileData );
    if( hFile == INVALID_HANDLE_VALUE ){
        ErrorMsg(TEXT("Expand Directory: FindFirstFile failure."));
        ExpDirExit(lpCInfo, hFile);
        return(0);
    }

    //
    // Walk all the files in the directory.
    //
    while( !fDone ){

        //
        // Check to see if the thread has been requested to kill itself.
        //  This code does not clear the suicide flag for synchronization
        //  reasons - it is left to the calling code.
        //
        if( lpCInfo->fSuicide ){
            ErrorMsg(TEXT("Expand Directory: killing thread per request."));
            ExpDirExit(lpCInfo, hFile);

            //
            // Post an MM_REFRESH Message if the user has re-collapsed the
            //  Dir LB.
            //
            if( !lpCInfo->fDirExpand )
                if( !PostMessage(lpCInfo->hwnd, WM_COMMAND, MM_REFRESH,
                                 (LPARAM)0) )
                    ErrorMsg(TEXT("ExpDir: MM_REFRESH call failure."));

            //
            // We must return failure (0) here, so if FullExpand is calling,
            //  it will terminate.
            //
            return(0);
        }

        //
        // Append filename to path, and get attributes
        //
        lstrcpy(lpszHold, FileData.cFileName);
        dwAttrib = GetFileAttributes( szFileName );

        //
        // Check if file is a directory.  If not, or if '.' or '..', fall
        //  through.  If so, add it to the directory listbox.
        //
        if( dwAttrib & FILE_ATTRIBUTE_DIRECTORY )
            if( lstrcmp( FileData.cFileName,TEXT(".") ) )
                if( lstrcmp( FileData.cFileName,TEXT("..") ) ){
                    TCHAR   szLBEntry[DIRECTORY_STRING_SIZE << 1];
TCHARszHidden[DIRECTORY_STRING_SIZE << 1];

lstrcpy(szHidden, szFileName2);
lstrcat(szHidden, FileData.cFileName);

                    if (IsEncrypted(szHidden)) {
                    TCHARszDecryptedName[BUF_SIZE];
HANDLEhFileRead;

GetDecryptedDirName(lpCInfo->hDirLB,
szHidden,
szDecryptedName,
0,
&hFileRead);
                    CloseHandle(hFileRead);

if (!SimplifyFileName(szDecryptedName, szBuf)) {
ErrorMsg(TEXT("ExpDir: SimplifyFileName failed."));
return FALSE;
}

                    lstrcpy(szDecryptedName, szBuf);

                    ConstructLBEntry(lDirDepth, szDecryptedName, szLBEntry);
                    }
else
ConstructLBEntry(lDirDepth, FileData.cFileName, szLBEntry);

                    //
                    //  Increment index in order to add subdir after
                    //  dirs just inserted.
                    //
                    lIndex++;

                    if( SendMessage(lpCInfo->hDirLB, LB_INSERTSTRING,
                                       (WPARAM)lIndex,
                                       (LPARAM)szLBEntry) < 0){
                        ErrorMsg(TEXT("Expand Directory: error inserting string."));
                        ExpDirExit(lpCInfo, hFile);
                        return(0);
                    }

                    if (!TableInsert(lpCInfo->DirTable, szHidden, szLBEntry, lIndex)) {
ErrorMsg(TEXT("ExpDir: TableInsert failed."));
return FALSE;
}
     }

        fDone = !FindNextFile( hFile, &FileData );
    }

    ExpDirExit(lpCInfo, hFile);
    return(1);
}

/***********************************************************************************\
* ExpDirExit()
*
* Performs clean-up operations for ExpDir, closing handles, etc.
\***********************************************************************************/

void ExpDirExit(LPCINFO lpCInfo, HANDLE hFile)
{
    if (!lpCInfo) {
    ErrorMsg(TEXT("ExpDirExit: lpCInfo is NULL."));
    return;
    }

    // Reset redraw flag. Post this message, to avoid synchro problems

    if( PostMessage(lpCInfo->hDirLB, WM_SETREDRAW, (WPARAM)TRUE,
                (LPARAM)0 ) < 0)
        ErrorMsg(TEXT("Expand Directory:  Clear redraw flag failure"));

    //
    // Close FindFirstFile session
    //
    if( hFile != NULL)
        FindClose(hFile);

    //
    // Release Dir LB Mutex
    //
    ReleaseMutex( lpCInfo->hDirMutex);
}

/***********************************************************************************\
* ConstructDirName()
*
* Builds the fully qualified path of the current directory, by walking back
*  through the Dir LB tree.
*
* Returns:  TRUE if successful, FALSE if error.
*           Returns the full directory path of given directory name in
*             lpszDirName.
\***********************************************************************************/

BOOL ConstructDirName(LPCINFO lpCInfo, LONG lIndex, LPTSTR lpszDirName)
{
    LONG    lDirDepth,      //  Depth of selected directory
            lSeekDepth = LONG_MAX;

    TCHAR   szFileName[DIRECTORY_STRING_SIZE << 1];   // file buffer
    LPTSTR  lpszInfoPtr,
            lpszHold;

    if (!lpCInfo) {
    ErrorMsg(TEXT("ConstructDirName: lpCInfo is NULL."));
    return FALSE;
    }

    //
    // Clear the directory name buffer
    //
    *lpszDirName = TEXT('\0');

    //
    // Walk up the entries in the listbox, constructing the full path from
    //   the bottom up.
    //
    while( lIndex >= 0 ){

        //
        // Get listbox text, and compute the depth of the directory
        //
        if( SendMessage( lpCInfo->hDirLB, LB_GETTEXT, (WPARAM)lIndex,
                            (LPARAM)szFileName ) < 0 ){
            ErrorMsg(TEXT(" Expand Directory:  Get listbox text failure"));
            return(0);
        }

        lDirDepth = GetDirDepth(szFileName, &lpszInfoPtr);
        if( lDirDepth == -1){
            ErrorMsg(TEXT("ConstructDirName: GetDirDepth failed"));
            return(0);
        }

        //
        // If we've reached the next level up, add to the directory name
        //
        if( lDirDepth < lSeekDepth ){
            lSeekDepth = lDirDepth;

            // check if we will exceed the size of our buffer
            if( lstrlen(lpszInfoPtr) + lstrlen(lpszDirName) >
                    (DIRECTORY_STRING_SIZE << 1) ){
                ErrorMsg(TEXT("ConstructDirName:  Exceeded Directory Size limit"));
                return(0);
            }

            // Find the end of the directory name
            lpszHold = &lpszInfoPtr[lstrlen(lpszInfoPtr)];

            // If we're not at the root, add a '\'
            if( lIndex && (*lpszDirName != TEXT('\0')) )
                *lpszHold++ = TEXT('\\');

            // Append the heretofore computed path to the end of the dir name
            lstrcpy( lpszHold, lpszDirName);
            // Copy the whole path so far back into the final buffer
            lstrcpy( lpszDirName, ++lpszInfoPtr);
        }

        // If the first level dir has been added, jump to the root,
        //   else go up to the previous entry in the listbox.
        if( lDirDepth == 1 )
            lIndex = 0;
        else
            lIndex--;
    }

return(TRUE);
}

/***********************************************************************************\
* GetDirDepth()
*
* Returns:  -1 if error
*           otherwise, the depth of the directory (i.e. root is depth 0,
*           c:\foo is depth 1, etc.  This is computed in perhaps not the
*           most efficient way, by counting the ' 's proceeding the name.
*
*           This function also returns in lpszDirName a pointer to the
*           end of the preceeding ' ' characters within the given listbox str.
*           If the function fails, this pointer value is undefined.
*
* The left shifted index is to skip the tab characters. See ConstructLBEntry().
\***********************************************************************************/

LONG GetDirDepth(LPTSTR lpszLBString, LPTSTR *lpszDirName)
{
    TCHAR cBar;
    LONG  lCount = 0;

    do{

        cBar = lpszLBString[lCount << 1];

        if( cBar == TEXT(' ') )
            lCount++;
        else
            if( cBar != TEXT('+') && cBar != TEXT('-') ){
                ErrorMsg(TEXT("GetDirDepth: string parse error"));
                return(-1);
            }

    }while( cBar != TEXT('+') && cBar != TEXT('-') );

    *lpszDirName = &(lpszLBString[lCount << 1]);

    return( lCount );
}

/***********************************************************************************\
* CollapseDir()
*
* If directory is expanded, collapses it, by deleteing any subdirectory
*  entries below it.
*
* Returns:  TRUE if successful, FALSE if error
\***********************************************************************************/

BOOL CollapseDir(LPCINFO lpCInfo, LONG lIndex, LONG lDirDepth)
{
    TCHAR   szFileName[DIRECTORY_STRING_SIZE << 1];   // file name buffer
    LPTSTR  lpszNamePtr;

    LONG    lDepthHold;

    if (!lpCInfo) {
    ErrorMsg(TEXT("CollapseDir: lpCInfo is NULL."));
    return FALSE;
    }

    //
    // Remove any following LB entries until we return to same depth
    //
    do{
    if (lpCInfo->DirTable->iNumElems == 1)
    return 1;

    if( SendMessage( lpCInfo->hDirLB, LB_GETTEXT, (WPARAM)lIndex + 1,
                            (LPARAM)szFileName ) < 0 ){
            ErrorMsg(TEXT(" Expand Directory:  Get listbox text failure"));
            return(0);
        }

        lDepthHold = GetDirDepth( szFileName, &lpszNamePtr);
        if( lDirDepth == -1 ){
            ErrorMsg(TEXT(" Expand Directory:  GetDirDepth failure"));
            return(0);
        }

        if( lDirDepth < lDepthHold ) {
            if( SendMessage( lpCInfo->hDirLB, LB_DELETESTRING, (WPARAM)lIndex + 1,
                                (LPARAM)0 ) < 0 ){
                ErrorMsg(TEXT(" Expand Directory:  Delete String failure"));
                return(0);
            }
if (!TableRemove(lpCInfo->DirTable, lIndex + 1)) {
ErrorMsg(TEXT("CollapseDir: TableRemove failed."));
return 0;
}
}
    }while( lDirDepth < lDepthHold );

    return(1);
}

/***********************************************************************************\
* ConstructLBEntry()
*
* Given the parent's directory depth, and the subdirectory name, inserts
* ' 's equal to the depth+1 of the parent (plus tab character), and an
* unexpanded '+' directory marker, then the name of the subdirectory.
*
* The left shifted index is to skip the tab characters.
*
* Returns:  void.  the completed listbox entry is returned in szLBEntry.
\***********************************************************************************/

void ConstructLBEntry(LONG lDirDepth, LPTSTR szFileName, LPTSTR szLBEntry)
{
    int i;

    for(i = 0; i <= lDirDepth; i++){
        szLBEntry[i << 1] = TEXT(' ');
        szLBEntry[(i << 1) + 1] = 9;
    }

    szLBEntry[i << 1] = TEXT('+');
    szLBEntry[(i << 1) + 1] = TEXT('\0');

    lstrcat(szLBEntry, szFileName);
}

/***********************************************************************************\
* FullExpand()
*
* From the unexpanded root, walks down directory LB, expanding each directory
*  until it reaches the end of the tree.
\***********************************************************************************/

BOOL FullExpand(LPCINFO lpCInfo)
{
    LONG lIndex = 0;

    if (!lpCInfo) {
    ErrorMsg(TEXT("FullExpand: lpCInfo is NULL."));
    return FALSE;
    }

    if( WaitForSingleObject( lpCInfo->hDirMutex, MUTEX_TIMEOUT)
            == WAIT_TIMEOUT ){
        ErrorMsg(TEXT("FullExpand: Dir LB Mutex Timeout."));
        return(0);
    }

    while( SendMessage(lpCInfo->hDirLB, LB_SETCURSEL,
                        (WPARAM)lIndex,
                        (LPARAM)0) != LB_ERR ){
        if( !ExpDir( lpCInfo ) ){
            ErrorMsg(TEXT("Full Expand: ExpDir failure."));
            ReleaseMutex( lpCInfo->hDirMutex );

            // This is in case the ExpDir failed because a change drive
            //  command caused a kill.
            if( !PostMessage(lpCInfo->hwnd, WM_COMMAND, MM_REFRESH,
                            (LPARAM)0) )
                ErrorMsg(TEXT("ExpDir: MM_REFRESH call failure."));
            return(0);
        }

        lIndex++;
    }

    //  Set selection in listboxes to first item.
    if( SendMessage(lpCInfo->hDirLB, LB_SETCURSEL,
                        (WPARAM)0,
                        (LPARAM)0) == LB_ERR ){
        ErrorMsg(TEXT("Full Expand: Dir LB Set Selection Error"));
        ReleaseMutex( lpCInfo->hDirMutex );
        return(0);
    }

    ReleaseMutex( lpCInfo->hDirMutex );
    return(1);
}


EXPDIR.H

// EXPDIR.H

#ifndef EXPDIR_INCLUDED
#define EXPDIR_INCLUDED

BOOL ExpDir(LPCINFO);
VOID ExpDirExit(LPCINFO, HANDLE);
BOOL ConstructDirName(LPCINFO, LONG, LPTSTR);
LONG GetDirDepth(LPTSTR, LPTSTR*);
BOOL CollapseDir(LPCINFO, LONG, LONG);
VOID ConstructLBEntry(LONG, LPTSTR, LPTSTR);
BOOL FullExpand(LPCINFO);

#endif


FILLFILE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// FILLFILE.C

#include "cfiler.h"

extern HANDLE   ghDrvThread;
extern HWND   ghwndDrv;

/********************************************************************************\
* FillFile()
*
* in parameters
* lpCInfo - instance data for drive child
* hwnd - HWND specified by caller
*
* purpose:
* Fills the file listbox with strings constructed from the contents of the
* directory that is highlighted in the directory listbox.
* This is accomplished by building a file string table with hidden and displayed
* string pairs. The hidden strings are the full pathnames as they exist on the
* disk. The displayed strings are specially formatted strings containing file
* and directory names to be displayed in the owner-draw listbox. The listbox
* knows how to appropriately draw these file and directory names because the
* strings have some special characters at the beginning. Once the string table
* has been constructed, a function called TableSend is called, which generates
* LB_ADDSTRING messages to the listbox, one for each displayed string in the
* table.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\********************************************************************************/

BOOL FillFile(LPCINFO lpCInfo, HWND hwnd) {
BOOLbHasSignature = FALSE;
    BOOLfNextFile = TRUE;
BOOLfSearchFlag = TRUE;
DWORDdwLastError = ERROR_SUCCESS;
HANDLEhFindFile;
HANDLEhFileRead;
INTi;
TCHARszDecryptedName[PATH_SIZE];
TCHARszFileName[PATH_SIZE];
TCHARszFind[PATH_SIZE];
TCHAR               szFiles[PATH_SIZE];
TCHARszBuf[PATH_SIZE];
WIN32_FIND_DATAFindFileData;
LONGlIndex;
TABLEsigtable;

    if (!lpCInfo) {
    ErrorMsg(TEXT("FillFile: lpCInfo is NULL."));
    return FALSE;
    }

    if( WaitForSingleObject(lpCInfo->hFileMutex, MUTEX_TIMEOUT)
                == WAIT_TIMEOUT) {
    ErrorMsg(TEXT("FillFile: File LB Mutex Timeout."));
    return 0;
    }

TableFree(lpCInfo->FileTable);

lpCInfo->FileTable = TableNew();

if (!lpCInfo->FileTable) {
ErrorMsg(TEXT("MM_FILLFILE: TableNew failed."));
return 0;
}

SendMessage(lpCInfo->hFileLB, LB_RESETCONTENT, (WPARAM)NULL, (LPARAM)NULL);

    // Put the full pathname of the directory into szFind
    // by accessing the directory string table.

    lIndex = SendMessage( lpCInfo->hDirLB, LB_GETCARETINDEX,
              (WPARAM)NULL, (LPARAM)NULL );

if (!TableGetHidden(lpCInfo->DirTable, lIndex, szFind)) {
ErrorMsg(TEXT("FillFile: TableGetHidden failed."));
return 0;
}

// if szFind has a '\' append a * otherwise append '\*'

if (szFind[lstrlen(szFind) - 1] == TEXT('\\'))
szFind[lstrlen(szFind) - 1] = TEXT('\0');

lstrcpy(szFiles, szFind);

lstrcat(szFind, TEXT("\\*"));

if ((hFindFile = FindFirstFile(szFind, &FindFileData))
== INVALID_HANDLE_VALUE) {
ReleaseMutex(lpCInfo->hFileMutex);
ErrorMsg(TEXT("FillFile: FindFirstFile failed."));
ReleaseMutex(lpCInfo->hFileMutex);
return 0;
}

sigtable = TableNew();

if (!sigtable) {
ErrorMsg(TEXT("FillFile: TableNew failed."));
return 0;
}

BuildSigTable(sigtable);

// Loop through, calling FindNextFiles until no more files are left.

for (i = 0; fNextFile || dwLastError != ERROR_NO_MORE_FILES; i++) {
lstrcpy(szFileName, szFiles);
lstrcat(szFileName, TEXT("\\"));
lstrcat(szFileName, FindFileData.cFileName);
bHasSignature = FALSE;

// If szFileName is not a directory, check if it has a signature.

if (!CFilerIsDirectory(szFileName))
HasSignature(szFileName, sigtable, &bHasSignature);

if (IsEncrypted(szFileName)) {
BOOL fIsValidEncryptedFile = TRUE;

if (!CFilerIsDirectory(szFileName)) {
if (!GetDecryptedFileName(hwnd, szFileName, szDecryptedName, &hFileRead)) {
fIsValidEncryptedFile = FALSE;
lstrcpy(szBuf, TEXT("\0"));
}
CloseHandle(hFileRead);
}
else
GetDecryptedDirName(hwnd, szFileName, szDecryptedName, 0, &hFileRead);

if (fIsValidEncryptedFile) {
if (!bHasSignature)
// '|' indicates to the the owner-draw listbox
// an encrypted file or directory
// This is not unwholesome since '|' is not a legal
// character for a file or directory identifier.
lstrcpy(szBuf, TEXT("|"));
else if (bHasSignature)
// '>' indicates an encrypted file that has been signed.
// This is not unwholesome since '>' is not a legal character
// for a file or directory identifier.
lstrcpy(szBuf, TEXT(">"));
}

lstrcat(szBuf, szDecryptedName);

// add to string table.

if (IsLegalToAdd(FindFileData.cFileName)) {
 if (!TableAdd(lpCInfo->FileTable, FindFileData.cFileName, szBuf)) {
ErrorMsg(TEXT("FillFile: TableAdd failed."));
return FALSE;
}
}
}
else {
// Not encrypted

if (CFilerIsDirectory(szFileName)) {

// For directories, enclose the directory name with brackets
// before inserting into string table.

lstrcpy(szBuf, TEXT("["));
lstrcat(szBuf, FindFileData.cFileName);
lstrcat(szBuf, TEXT("]"));

// Add directory name to string table.

if (IsLegalToAdd(FindFileData.cFileName)) {
if (!TableAdd(lpCInfo->FileTable, szBuf, szBuf)) {
ErrorMsg(TEXT("FillFile: TableAdd failed."));
return FALSE;
}
}
}
else if (!CFilerIsDirectory(szFileName)) {

// Not a directory and not encrypted

if (!bHasSignature)
lstrcpy(szBuf, FindFileData.cFileName);
else if (bHasSignature) {
// ';' indicates a signed file. This is
// not unwholesome since ';' is not a legal
// character for a filename.
lstrcpy(szBuf, TEXT(";"));
lstrcat(szBuf, FindFileData.cFileName);
}
// Add filename to string table.

if (IsLegalToAdd(FindFileData.cFileName)) {
 if (!TableAdd(lpCInfo->FileTable, FindFileData.cFileName, szBuf)) {
ErrorMsg(TEXT("FillFile: TableAdd failed."));
return FALSE;
}
}
}
}

fNextFile = FindNextFile(hFindFile, &FindFileData);

dwLastError = GetLastError();
}

// sort the file string table on its displayed members
// using the same string compare routine
// that the owner-draw listbox uses.

if (!TableSort(lpCInfo->FileTable, CFilerlstrcmp)) {
ErrorMsg(TEXT("FillFile: selection failed."));
return 0;
}

// send all of the displayed members of the file string table
// to the owner-draw listbox.
// It does not matter in what order they are sent since the
// owner-draw listbox will sort them anyway.

if (GetSize(lpCInfo->FileTable) && !TableSend(lpCInfo->FileTable, lpCInfo->hFileLB)) {
ErrorMsg(TEXT("FillFile: TableSend failed."));
return 0;
}

//
//  Set selection to first file.
//
if (GetSize(lpCInfo->FileTable)) {
if(SendMessage(lpCInfo->hFileLB, LB_SETSEL, (WPARAM)TRUE, (LPARAM)0) == LB_ERR) {
ErrorMsg(TEXT("FillFile:  Listbox selection failure."));
ReleaseMutex( lpCInfo->hFileMutex );
return 0;
}
}

if (!SetWindowText(lpCInfo->hTextWnd, lpCInfo->CaptionBarText)) {
ErrorMsg(TEXT("FillFile: SetWindowText failed."));
return FALSE;
}

TableFree(sigtable);

ReleaseMutex( lpCInfo->hFileMutex );

FindClose(hFindFile);

return TRUE;
}

/************************************************************************\
* BuildSigTable()
*
* input:
* sigtable - pointer to an empty string table
*
* purpose:
* Builds a string table with displayed fields containing
* the filenames of all the files in the \sig directory on the
* same drive as the system directory.
*
* returns
* TRUE if successful
* FALSE if unsuccessful
\************************************************************************/

BOOL BuildSigTable(TABLE sigtable)
{
WIN32_FIND_DATA FindFileData;
HANDLE hFindFile;
TCHAR szFiles[PATH_SIZE];
TCHAR szFileName[PATH_SIZE];
TCHAR szSystemDirectory[PATH_SIZE];
TCHAR szSigDir[PATH_SIZE];

// [system directory drive]:\sig -> szSigDir

GetSystemDirectory(szSystemDirectory, PATH_SIZE * sizeof(*szSystemDirectory));
szSigDir[0] = szSystemDirectory[0];
szSigDir[1] = TEXT('\0');
lstrcat(szSigDir, TEXT(":\\sig"));
lstrcpy(szFiles, szSigDir);
lstrcat(szFiles, TEXT("\\*"));

hFindFile = FindFirstFile(szFiles, &FindFileData);

if (hFindFile == INVALID_HANDLE_VALUE) {
return FALSE;
}

// insert all the signature filenames into the table sigtable.

do {
lstrcpy(szFileName, szSigDir);
lstrcat(szFileName, TEXT("\\"));
lstrcat(szFileName, FindFileData.cFileName);

TableInsert(sigtable, TEXT("NULL"), szFileName, 0);
} while (FindNextFile(hFindFile, &FindFileData)
|| GetLastError() != ERROR_NO_MORE_FILES);

FindClose(hFindFile);
}

/*************************************************************************************\
* GetLBText()
*
* Gets the text of the currently selected (careted) item in the given
*   listbox.
*
* Returns:  Index of selected item if successful, -1 on failure
\*************************************************************************************/

LONG GetLBText(HWND hActiveLB, PTCHAR szItemBuff)
{
    LONG    lIndex;

    //
    // Retrieve selected (careted) item.
    //
    lIndex = SendMessage( hActiveLB, LB_GETCARETINDEX,
                          (WPARAM)NULL, (LPARAM)NULL );

    if( SendMessage( hActiveLB, LB_GETTEXT, (WPARAM)lIndex,
                     (LPARAM)szItemBuff) == LB_ERR ){
        ErrorMsg(TEXT("LBN_DBLCLK: Text retrieval failure."));
        return(-1);
    }

    return( lIndex );
}

/*************************************************************************************\
* UpdateFileLB()
*
* Updates the file listbox of the drive child given by sending an MM_FILLFILE
*   message to it.
*
*  input:   hwnd    -   Handle of drive child to update file listbox of.
\*************************************************************************************/

VOID UpdateFileLB(HWND hwnd)
{
    LPCINFO lpCInfo;

    lpCInfo = (LPCINFO)GetWindowLong(hwnd, GWL_USERDATA);

SendMessage(hwnd, WM_COMMAND, (WPARAM)MM_FILLFILE, (LPARAM)0);
}

/*************************************************************************************\
* RunListBoxItem()
*
*  input:   lpCInfo   -   pointer to Drv child's LPCINFO structure
*  cmd  -   integer specifying what action to perform on
*  the file corresponding to the selected
*  file listbox text.
*
* purpose:
* 1. Obtains the full pathname of the selected directory or file
* in the file listbox.
* 2. Depending on whether a directory or file has been selected and
* depending on cmd, the appropriate action is taken:
* - CFILEREncryptFile()
* - EncryptDir()
* - CFILERDecryptFile()
* - DecryptDir()
* - SignFile()
* - VerifyFile()
*
* Returns TRUE if successful, FALSE if unsuccessful.
* If a user tries to sign a directory, RunListBoxItem returns FALSE.
\*************************************************************************************/

BOOL RunListBoxItem(LPCINFO lpCInfo, int cmd)
{
    LONGlIndex;
TCHARszBuf[PATH_SIZE];
    TCHARszFileName[PATH_SIZE];
TCHARszBuf2[PATH_SIZE];

    if (!lpCInfo) {
    ErrorMsg(TEXT("RunListBoxItem: lpCInfo is NULL."));
    return FALSE;
    }

    // get index of selected item in Directory listbox.

    lIndex = SendMessage( lpCInfo->hDirLB, LB_GETCARETINDEX,
                          (WPARAM)NULL, (LPARAM)NULL );

// obtain full pathname of directory corresponding to selected text in
// directory listbox by accessing the string table for the directory listbox.

if (!TableGetHidden(lpCInfo->DirTable, lIndex, szFileName)) {
ErrorMsg(TEXT("RunListBoxItem: TableGetHidden failed."));
return FALSE;
}

   // append a '\\' to the end of the full pathname if one isn't already there.

   if (szFileName[lstrlen(szFileName) - 1] != TEXT('\\')) {
   if (!lstrcat(szFileName, TEXT("\\"))) {
ErrorMsg(TEXT("RunListBoxItem: lstrcat failed."));
return FALSE;
}
  }

    //
    // Retrieve selected (careted) item.
    //

    // get index of selected item in the file listbox.

    lIndex = GetLBText(lpCInfo->hFileLB, szBuf);

    if (lIndex == LB_ERR) {
ErrorMsg(TEXT("RunListBoxItem: GetLBText failed."));
        return FALSE;
}

    // get the full pathname of the file corresponding to the selected text
    // in the file listbox by accessing the string table for the
    // file listbox.

   if (!TableGetHidden(lpCInfo->FileTable, lIndex, szBuf)) {
ErrorMsg(TEXT("RunListBoxItem: TableGetHidden failed."));
return FALSE;
}

if (WaitForSingleObject(ghDrvThread, INFINITE) == WAIT_FAILED) {
ErrorMsg(TEXT("RunListBoxItem: WaitForSingleObject failed."));
return FALSE;
}

if (lpCInfo->hDirThread
&& WaitForSingleObject(lpCInfo->hDirThread, INFINITE) == WAIT_FAILED) {
ErrorMsg(TEXT("RunListBoxItem: WaitForSingleObject failed."));
return FALSE;
}

lstrcpy(szBuf2, szBuf);
SimplifyFileName(szBuf2, szBuf);
lstrcat(szFileName, szBuf);

// if szFileName is not encrypted and the user wants to encrypt/decrypt
if (!IsEncrypted(szFileName) && cmd == ENCRYPT_DECRYPT) {
// if szFileName is a directory
if (CFilerIsDirectory(szFileName)) {
// encrypt the directory
if (!EncryptDir(ghwndDrv, szFileName, lpCInfo)) {
ErrorMsg(TEXT("RunListBoxItem: EncryptDir failed."));
return FALSE;
}
}
// not a directory
else {
// encrypt the file
if(!CFILEREncryptFile(ghwndDrv, szFileName)) {
ErrorMsg(TEXT("RunListBoxItem: CFILEREncryptFile failed."));
return FALSE;
}
}
UpdateFileLB(ghwndDrv);
    }
else if (cmd == ENCRYPT_DECRYPT) {
// if szFileName is a directory
if (CFilerIsDirectory(szFileName)) {
// decrypt the directory.
if (!DecryptDir(ghwndDrv, szFileName, lpCInfo)) {
ErrorMsg(TEXT("RunListBoxItme: DecryptDir failed."));
return FALSE;
}
}
else {
// not a directory
if (!CFILERDecryptFile(ghwndDrv, szFileName)) {
 ErrorMsg(TEXT("RunListBoxItem: CFILERDecryptFile failed."));
 return FALSE;
}
}
UpdateFileLB(ghwndDrv);
}
else if (cmd == SIGN) {
// sign file if szFileName is not a directory
if (!CFilerIsDirectory(szFileName) && !SignFile(ghwndDrv, szFileName)) {
 ErrorMsg(TEXT("RunListBoxItem: SignFile failed."));
 return FALSE;
}
UpdateFileLB(ghwndDrv);
}
else if (cmd == VERIFY) {
// verify file is szFileName is not a directory.
if (!CFilerIsDirectory(szFileName) && !VerifyFile(ghwndDrv, szFileName)) {
 ErrorMsg(TEXT("RunListBoxItem: VerifyFile failed."));
 return FALSE;
}
UpdateFileLB(ghwndDrv);
}

    return TRUE;
}

/*************************************************************************************\
* CFilerIsDirectory()
*
* input - szDirName: In Parameter: Buffer containing full pathname of directory
*
* Purpose: returns TRUE if szDiretory is a directory or a hidden directory,
* FALSE otherwise or if error.
\*************************************************************************************/

BOOL CFilerIsDirectory(LPTSTR szDirName) {
DWORD dwFileAttributes;

if (!szDirName) {
ErrorMsg(TEXT("CFilerIsDirectory: szDirName is NULL."));
return FALSE;
}

dwFileAttributes = GetFileAttributes(szDirName);

if (dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
return TRUE;

if (dwFileAttributes == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN))
return TRUE;

return FALSE;
}

/*************************************************************************************\
* SeparatePathName()
*
* First parameter (in parameter): szFileName - full path name to be separated
*
* Second parameter (out parameter): szDir - buffer to hold full pathname of directory
*
* Third parameter (out parameter): szFile - buffer to hold filename (not full pathname)
*
* Example: C:\directory\[foo] -> C:\directory + [foo]
\*************************************************************************************/

BOOL SeparatePathName(LPTSTR szFileName, LPTSTR szDir, LPTSTR szFile) {
INTi, j, nLen, nFileOffset;

if (!szFileName) {
ErrorMsg(TEXT("SeparatePathName: szFileName is NULL."));
return FALSE;
}

if (!szDir) {
ErrorMsg(TEXT("SeparatePathName: szDir is NULL."));
return FALSE;
}

if (!szFile) {
ErrorMsg(TEXT("SeparatePathName: szFile is NULL."));
return FALSE;
}

lstrcpy(szDir, TEXT("\0"));
lstrcpy(szFile, TEXT("\0"));

nLen = lstrlen(szFileName);

if (szFileName[nLen - 1] == TEXT('\\'))
for (i = nLen - 1; szFileName[i] != TEXT('\\') && i; i--);
else
for (i = nLen; szFileName[i] != TEXT('\\') && i; i--);

nFileOffset = i + 1;

    for (i = 0; i < nFileOffset - 1; i++)
    szDir[i] = szFileName[i];
    szDir[i] = TEXT('\0');

    for (j = 0, i++; i < lstrlen(szFileName); i++, j++)
        szFile[j] = szFileName[i];
    szFile[j] = TEXT('\0');

return TRUE;
}

/*************************************************************************************\
* SeparatePathName2()
*
* First parameter (in parameter): szFileName - full path name to be separated
*
* Second parameter (out parameter): szDir - buffer to hold full pathname of directory
*
* Third parameter (out parameter): szFile - buffer to hold filename (not full pathname)
*
* brackets are stripped from the filename before the filename is inserted into szFile.
*
* C:\directory\[foo] -> C:\directory + foo
\*************************************************************************************/

BOOL SeparatePathName2(LPTSTR szFileName, LPTSTR szDir, LPTSTR szFile) {
INTi, j, nLen, nFileOffset;

if (!szFileName) {
ErrorMsg(TEXT("SeparatePathName2: szFileName is NULL."));
return FALSE;
}

if (!szDir) {
ErrorMsg(TEXT("SeparatePathName2: szDir is NULL."));
return FALSE;
}

if (!szFile) {
ErrorMsg(TEXT("SeparatePathName2: szFile is NULL."));
return FALSE;
}

lstrcpy(szDir, TEXT("\0"));
lstrcpy(szFile, TEXT("\0"));

nLen = lstrlen(szFileName);

if (szFileName[nLen - 1] == TEXT('\\'))
for (i = nLen - 1; szFileName[i] != TEXT('\\') && i; i--);
else
for (i = nLen; szFileName[i] != TEXT('\\') && i; i--);

nFileOffset = i + 1;

    for (i = 0; i < nFileOffset - 1; i++)
    szDir[i] = szFileName[i];
    szDir[i] = TEXT('\0');

    for (j = 0, i++; i < lstrlen(szFileName); ) {
    if (szFileName[i] == TEXT('[') || szFileName[i] == TEXT(']'))
i++;
    else {
    szFile[j] = szFileName[i];
    i++;
j++;
}
}

    szFile[j] = TEXT('\0');

return TRUE;
}

/*************************************************************************************\
* IsEncrypted()
*
* input:
*
* szFileName - In parameter: buffer containing filename
*
* If szFileName has a .CRP extension, IsEncrypted() returns TRUE, FALSE otherwise
* or if failure.
\*************************************************************************************/

BOOL IsEncrypted(LPTSTR szFileName) {
INT nLen;

if (!szFileName) {
ErrorMsg(TEXT("IsEncrypted: szFileName is NULL."));
return FALSE;
}

nLen = lstrlen(szFileName);

if (szFileName[nLen - 4] == TEXT('.') &&
  szFileName[nLen - 3] == TEXT('C') &&
szFileName[nLen - 2] == TEXT('R') &&
szFileName[nLen - 1] == TEXT('P'))
return TRUE;

return FALSE;
}

/*************************************************************************************\
* IsLegalToAdd()
*
* input
* in parameters
* szFileName - buffer containing filename (not full pathname) of file in question
*
* purpose
* determines whether szFileName is a file or directory that should be added to
* the file listbox string table.
*
* returns
* TRUE if szFileName is legal
* FALSE otherwise
\*************************************************************************************/

BOOL IsLegalToAdd(LPTSTR szFileName)
{
if (lstrcmp(szFileName, TEXT(".."))
 && lstrcmp(szFileName, TEXT("."))
 && lstrcmp(szFileName, TEXT("dirinfo")))
 return TRUE;

return FALSE;
}


FILLFILE.H

// FILLFILE.H

#ifndef FILLFILE_INCLUDED
#define FILLFILE_INCLUDED

LONG    GetLBText           (HWND, PTCHAR);
BOOLmyIsDirectory(LPTSTR);
BOOL SeparatePathName(LPTSTR, LPTSTR, LPTSTR);
BOOL SeparatePathName2(LPTSTR, LPTSTR, LPTSTR);
BOOL FillFile(LPCINFO, HWND);
BOOL CFilerIsDirectory(LPTSTR);
BOOL IsEncrypted(LPTSTR);
BOOL IsLegalToAdd(LPTSTR);
BOOL BuildSigTable(TABLE);

#endif // #ifdef FILLFILE_INCLUDED


GLOBALS.H

// GLOBALS.H

#ifndef GLOBALS_INCLUDED
#define GLOBALS_INCLUDED

#include "TABLE.H"

#define DEBUGMSGS
#define DEBUGGING
#define NOSTRICT
#define BUF_SIZE 1000
#define PATH_SIZE 600
#define HEADER_SIZE 256
#define SHA_SIZE 20
#define RANDOM_NAME_LENGTH 8
#defineEXPORTER
#defineENCRYPT_DECRYPT 1
#defineSIGN2
#defineVERIFY3
#define MAXBLOBLEN200

//
// Accellerator IDs
//
#define ACCEL_ID        1001

//
// Icon IDs
//
#define UI_CRYPTFILERICON    2001

//
// DialogBox Control IDs
//
#define SB_OK           3001
#define SB_CANCEL       3002
#define SB_ACTION       3003
#define SB_TOFROM       3004
#define SB_SOURCE       3005
#define SB_DEST         3006
#define SB_STATIC       3007
#define SB_KEY          3008
#define SB_VALUE        3009
#define SB_SCROLL       3010

//
// String Table IDs
//
#define STR_EXE         4001
#define STR_COM         4002
#define STR_CMD         4003
#define STR_BAT         4004

#define STR_DEF_EDITOR  4101

//
// Bitmap IDs
//
#define UB_BMP_MARKER   5001    // This should be the same as the first bitmap.
#define UB_FLOPPY1      5001    // THESE BITMAP IDs MUST BE CONTIGUOUS AND
#define UB_FLOPPY2      5002    //   IN THIS ORDER!  For 'ease of loading'.
#define UB_REMOTE1      5003
#define UB_REMOTE2      5004
#define UB_CD1          5005
#define UB_CD2          5006
#define UB_FIXED1       5007
#define UB_FIXED2       5008

//
// Command line window and Drv-CHILD child window IDs
//
#define COMMAND_ID      6001    // Command Line Window
#define TEXT_WINDOW_ID  6002    // Title Bar
#define LISTDIR_ID      6003    // Directory ListBox
#define LISTFILE_ID     6004    // File ListBox

//
// Internal Main window IDs
//
#define MM_FILLDIR      7001
#define MM_FILLFILE     7002

//
// Menu ID's, VKEYs, & other WM_COMMAND messages
//
#define MM_COPY         8002
#define MM_DELETE       8003
#define MM_MOVE         8004
#define MM_RENAME       8005
#define MM_MKDIR        8006
#define MM_DELDIR       8007
#define MM_VERSION      8008
#define MM_EXIT         8009

#define MM_REFRESH      8101
#define MM_ACTIVEDRV    8102
#define MM_TAB          8103
#define MM_SWAP         8104
#define MM_ORIENT       8105
#define MM_EXPAND       8106
#define MM_KEEPCMD      8107

#define MM_ESCAPE       8112
#define MM_TOGGLE       8113

#define MM_ABOUT        8201

#define MM_DRIVE_NUM    8301       // ...Through 8226, one for each drive letter
                    //   .
                    //   .
                    //  8326

//
// constants
//
#define MAIN_WIDTH          605    // Default width and height of main window
#define MAIN_HEIGHT         400

#define DRIVE_BITMAP_WIDTH  23     // Width and height of drive toolbar bitmaps
#define DRIVE_BITMAP_HEIGHT 14     //   assoc. w/ bitmap IDs above.
#define DRIVE_LETTER_SPACING 2     // Space between drv bmp and drv letter label
#define DRIVE_BITMAP_SPACING 6     // Space on either side of drv bitmap-labels
#define NUM_BITMAPS          8     // # types of Drive Bitmaps to be loaded.

#define DIRECTORY_STRING_SIZE MAX_PATH  // Size of max allowable direcory path

#define LIST_BORDER         2      // Space between listbox and Drv child frame

#define DRIVE_MENU_NUM      2      // This is the 'Drives' submenu

#define LISTBOX_TAB_SIZE    8      // Size of the tabs in the Drv child LBs

#define OVER_UNDER          0      // Relative positions of Drv children
#define SIDE_BY_SIDE        1

#define BOLD_FONT           700    // GetDeviceCaps sez this value is for bold
                                   //   in the lfWeight LOGFONT member

#define NUM_EXTENSION_STRINGS   4  // Lists number of file ext. in stringtable
#define EXTENSION_LENGTH        5  // Tot. number of characters in extension,
                                   //  including '\0' char.

#define FILE_SYSTEM_STRING_SIZE         20      // Must be >= 8

#define NUM_POSSIBLE_REMOVABLE_DRIVES   26      // A-Z

#define MUTEX_TIMEOUT       50     // LB Mutex timeout in milliseconds

// The following are used in the .EXE Version retrieval code in DRVPROC.C
#define NUM_VERSION_INFO_KEYS    12     // See FilerGetVersion()
#define VERSION_DLG_LB_HEIGHT    10     // height in text lines of version dlg LBs

#define VERSION_INFO_KEY_ROOT    TEXT("\\StringFileInfo\\")
#define VERSION_INFO_LANG_ID TEXT("040904B0")
// end .EXE Version code constants

//
// Drive enumeration strucure
//
typedef struct _PerDriveInfo {
    TCHAR   DriveLetter;
    LPTSTR  DriveName;
    UINT    DriveType;
    struct _PerDriveInfo *next;
} DRVINFO, *LPDINFO;

//
// Instance data for each Drv child window
//
typedef struct _PerWndInfo {
    HWND    hwnd;                       // Handle to Drive child window
    HWND    hParent;                    // Handle to Filer main window
    HWND    hTextWnd;                   // Handle to Drive child Title Bar
    HWND    hFileLB;                    // Handle to Drv child File LB
    HWND    hDirLB;                     // Handle to Drv child Dir LB
    HANDLE  hDirThread;                 // Handle to ExpDir or FullExpand thread
    HANDLE  hDirMutex;                  // Protects Dir LB from mult. access
    HANDLE  hFileMutex;                 // Protects File LB from mult. access
    BOOL    fDirLeft;                   // Flag: the Dir LB is on left
    BOOL    fDirExpand;                 // Flag: the Dir LB is fully expanded
    BOOL    fSuicide;                   // Flag: kills ExpDir/FullExpand thread
    BOOL    fEscape;                    // Flag: User aborted Expand thread
    LPDINFO lpDriveInfo;                // pointer to current _PerDriveInfo
    TCHAR   CaptionBarText[BUF_SIZE]; // Drv Child Title text
TABLEDirTable;
TABLEFileTable;
} DRVCHILDINFO, *LPCINFO;

typedef struct _PerButtonInfo {
    HANDLE          hButton;
    BOOL            fButtonDown;
    LPDINFO         lpDrive;
    struct _PerButtonInfo  *next;
}   BINFO, *LPBINFO;

typedef struct _SelectDlgInfo {
    HWND    hwnd;
    DWORD   dwAction;
    LPTSTR  szAction;
    LPTSTR  szToFrom;
} SELECTINFO, *LPSINFO;

//  .EXE version information key structure: for FilerGetVersion() in DRVPROC.C
typedef struct _VersionKeyInfo {
    TCHAR const *szKey;
    TCHAR       *szValue;
} VKINFO, *LPVKINFO;

#endif


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by CFILER.RC
//
#define MM_ENCRYPT_DECRYPT              101
#define ID_SIGN                         102
#define ID_VERIFY                       103
#define MM_SIGN                         104
#define MM_VERIFY                       105
#define IDD_DIALOG18                    106
#define MM_PASS                         107
#define IDD_DIALOG16                    108
#define ID_HIDE                         108
#define IDD_DIALOG15                    109
#define ID_HELP_CONTENTS                109
#define ID_HELP                         109
#define IDD_DIALOG17                    113
#define IDD_DIALOG14                    114
#define IDD_DIALOG13                    115
#define IDD_DIALOG12                    116
#define IDD_DIALOG11                    117
#define IDD_DIALOG10                    119
#define IDC_RC2                         1000
#define IDC_RC4                         1001
#define IDC_EPROGRESS                   1001
#define IDC_DES                         1002
#define IDC_MD4                         1003
#define IDC_DPROGRESS                   1003
#define IDC_MD5                         1004
#define IDC_SHA                         1005
#define IDC_EDIT3                       1008
#define IDC_EDIT1                       1009
#define IDC_PASS                        1010
#define IDC_EDIT2                       1011
#define ID_ENCRYPTION_ALGORITHM         40008
#define ID_SIGNATURE_ALGORITHM          40011
#define IDC_STATIC                      -1
#define IDC_PASSTEXT                    65535

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         110
#define _APS_NEXT_CONTROL_VALUE         1004
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


TABLE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

// TABLE.C

#include "cfiler.h"
#include <malloc.h>

/******************************************************************\
* CheckHeap()
*
* input:
*
* message - buffer containing information to display on the
* debug window
*
* purpose:
*
* Calls _heapchk and outputs a formatted message onto the
* debug window.
\******************************************************************/

VOID CheckHeap(LPTSTR message) {
INT rv = _heapchk();

if (rv == _HEAPBADBEGIN) {
ErrorMsg(message);
ErrorMsg(TEXT("Initial header information is bad or cannot be found."));
return;
}
else if (rv == _HEAPBADNODE) {
ErrorMsg(message);
ErrorMsg(TEXT("Bad node has been found or heap is damaged."));
return;
}
else if (rv == _HEAPBADPTR) {
ErrorMsg(message);
ErrorMsg(TEXT("Pointer into heap is not valid."));
return;
}
else if (rv == _HEAPEMPTY) {
ErrorMsg(message);
ErrorMsg(TEXT("Heap has not been initialized."));
return;
}
else if (rv == _HEAPOK) {
ErrorMsg(message);
ErrorMsg(TEXT("Heap appears to be consistent."));
return;
}
else {
ErrorMsg(message);
ErrorMsg(TEXT("_heapcheck did not return a valid value."));
}
return;
}

/******************************************************************\
* GetSize()
*
* in parameters:
* pTable - pointer to string table
*
* returns
* number of elements of pTable
* -1 if error
\******************************************************************/

INT GetSize(TABLE pTable) {
if (!pTable) {
ErrorMsg(TEXT("GetSize: pTable is NULL."));
return -1;
}

return pTable->iNumElems;
}

/******************************************************************\
* TableNew()
*
* Returns a new string table with no elements or NULL
* if unsuccessful
\******************************************************************/

TABLE TableNew(VOID) {
TABLE pTable;

pTable = malloc(sizeof(*pTable) + 100);

if (!pTable) {
ErrorMsg(TEXT("TableNew: malloc failed."));
return FALSE;
}

pTable->iNumElems = 0;
pTable->pCurrentFiles = NULL;

return pTable;
}

/******************************************************************\
* TableFree()
*
* input: pTable - pointer to a table
*
* purpose:
*
* Frees the string table pointed to by pTable.
*
* return value:
*
* TRUE if successful
* FALSE if unsuccessful
\******************************************************************/

BOOL TableFree(TABLE pTable) {
ELEM *p, *q;

    if (!pTable) {
    ErrorMsg(TEXT("TableFree: pTable is NULL."));
    return FALSE;
    }

    for (p = pTable->pCurrentFiles; p; p = q) {
q = p->next;
free(p->hidden);
free(p->displayed);
free(p);
p = NULL;
}

free(pTable);

pTable = NULL;

return TRUE;
}

/******************************************************************\
* TableFind()
*
* input: pTable - pointer to a table
* szFindString - string in question.
*
* purpose:
* Searches for szFindString in table pTable.
*
* return value:
*
* TRUE if szFindString is in table
* FALSE if otherwise
\******************************************************************/

BOOL TableFind(TABLE pTable, LPTSTR szFindString) {
ELEM *p, *q;

    if (!pTable) {
    ErrorMsg(TEXT("TableFree: pTable is NULL."));
    return FALSE;
    }

    for (p = pTable->pCurrentFiles; p; p = q) {
q = p->next;
if (!CFilerlstrcmp(p->displayed, szFindString)) {
return TRUE;
}
}

return FALSE;
}

/******************************************************************\
* TableAdd()
*
* input:
*
* pTable - pointer to a string table
*
* szHidden, szDisplayed: pair of strings to insert into table
*
*
* purpose:
*
* Inserts a string pair at the end of the string table pointed
* to by pTable.
\******************************************************************/

BOOL TableAdd(TABLE pTable, TCHAR *szHidden, LPTSTR szDisplayed) {
return TableInsert(pTable, szHidden, szDisplayed, pTable->iNumElems);
}

/******************************************************************\
* TableInsert()
*
* input:
*
* pTable - pointer to a string table
* szHidden, szDisplayed : string pair to be inserted
* i - position to insert the string pair.
*
* purpose:
* inserts a string pair into the table pointed to by pTable
* at position indicated by i.
\******************************************************************/

BOOL TableInsert(TABLE pTable, LPTSTR szHidden, LPTSTR szDisplayed, INT i) {
ELEM *new, *p = NULL, *q = NULL;
INT j;

if (!pTable) {
ErrorMsg(TEXT("TableInsert: pTable is NULL."));
return FALSE;
}

new = malloc(sizeof(*new) + 2);

new->hidden = malloc(3 + sizeof(*szHidden) * lstrlen(szHidden));
new->displayed = malloc(3 + sizeof(*szDisplayed) * lstrlen(szDisplayed));
new->next = NULL;

lstrcpy(new->hidden, szHidden);
lstrcpy(new->displayed, szDisplayed);

if (!pTable->pCurrentFiles) {
pTable->pCurrentFiles = new;
pTable->iNumElems++;
return TRUE;
}

if (!i) {
ELEM *t;
t = pTable->pCurrentFiles;
pTable->pCurrentFiles = new;
new->next = t;
pTable->iNumElems++;
return TRUE;
}

for (j = 0, p = pTable->pCurrentFiles; j < i; j++, p = p->next)
q = p;

q->next = new;
new->next = p;

pTable->iNumElems++;

return TRUE;
}

/******************************************************************\
* TableRemove()
*
* input:
* pTable - pointer to a string table
* i - index of element to be removed
*
* purpose:
* Removes the element at position i in the string table
* pointed to by pTable.
*
* Return values:
* TRUE if successful
* FALSE if unsuccessful
\******************************************************************/

BOOL TableRemove(TABLE pTable, INT i) {
INT j;
ELEM *p, *q;

if (!pTable) {
ErrorMsg(TEXT("TableRemove: pTable is NULL."));
return FALSE;
}

if (!pTable->pCurrentFiles) {
pTable->iNumElems = 0;
return TRUE;
}

if (!i) {
ELEM *t = pTable->pCurrentFiles->next;
p = pTable->pCurrentFiles;
free(p->hidden);
free(p->displayed);
free(p);
pTable->pCurrentFiles = t;
pTable->iNumElems--;
return TRUE;
}

for (j = 0, p = pTable->pCurrentFiles; j < i; j++, p = p->next) {
q = p;
}

q->next = p->next;
free(p->hidden);
free(p->displayed);
free(p);

pTable->iNumElems--;

return TRUE;
}

/******************************************************************\
* TableSort()
*
* input:
* pTable - pointer to a string table
* compare - pointer to a compare function to be used in the
* sort process
*
* Purpose:
* sorts the elements of the string table pointed to by pTable
* on the displayed field.
*
* Return values:
* TRUE if successful
* FALSE if unsuccessful
\******************************************************************/

BOOL TableSort(TABLE pTable, INT compare(LPCTSTR , LPCTSTR)) {
ELEM *p, *q, *min, *t;
INT i, j;

if (!pTable) {
ErrorMsg(TEXT("TableSort: pTable is NULL."));
return FALSE;
}

t = malloc(sizeof(*t));

for (i = 0, p = pTable->pCurrentFiles; p && i < pTable->iNumElems - 1; i++, p = p->next) {
min = p;
for (q = p->next, j = 0; q && j < pTable->iNumElems; j++, q = q->next) {
if (!q->displayed || !p->displayed) {
ErrorMsg(TEXT("TableSort: parameters to compare are null."));
return FALSE;
}
if (compare(q->displayed, min->displayed) < 0) min = q;
}
t->displayed = min->displayed;
t->hidden = min->hidden;
min->displayed = p->displayed;
min->hidden = p->hidden;
p->displayed = t->displayed;
p->hidden = t->hidden;
}

free(t);

return TRUE;
}

/******************************************************************\
* TableSend()
*
* input:
* pTable - pointer to a string table
* mybox - handle to the listbox to which LB_ADDSTRING
* messages are to be sent
*
* Purpose:
* Sends the displayed members of the string table pointed to by
* pTable to the listbox whose handle is mybox
* by sending LB_ADDSTRING messages to the listbox for each
* element of the table.
*
* Return values:
* TRUE if successful
* FALSE if unsuccessful
\******************************************************************/

BOOL TableSend(TABLE pTable, HWND mybox) {
int j;
ELEM *p;
LRESULT result;

if (!pTable) {
ErrorMsg(TEXT("TableSend: pTable is NULL."));
return FALSE;
}

for (j = 0, p = pTable->pCurrentFiles; j < pTable->iNumElems; j++, p = p->next) {
result = SendMessage(mybox, LB_ADDSTRING, (WPARAM)0, (LPARAM)p->displayed);

if (result == LB_ERR) {
ErrorMsg(TEXT("TableSend: LB_ADDSTRING failed."));
return FALSE;
}
}

return TRUE;
}

/******************************************************************\
* TableGetHidden()
*
* input:
* pTable - pointer to a string table
* i - index of hidden element to retrieve
* rv - (out parameter): buffer to store the hidden string
*
* Purpose:
* Retrieves the hidden memeber of the table stored at position i
* in the string table pointed to by pTable.
*
* Returns:
* TRUE if successful
* FALSE if unsuccessful
\******************************************************************/

BOOL TableGetHidden(TABLE pTable, INT i, LPTSTR rv) {
INT j;
ELEM *p, *q;

if (!pTable) {
ErrorMsg(TEXT("TableGetHidden: pTable is NULL."));
return FALSE;
}

q = pTable->pCurrentFiles;

if (q) {
for (p = pTable->pCurrentFiles, j = 0; j <= i; j++, p = p->next)
q = p;
}
else {
ErrorMsg(TEXT("TableGetHidden: pTable->pCurrentFiles is NULL."));
lstrcpy(rv, TEXT("error"));
return FALSE;
}

if (q && q->hidden)
lstrcpy(rv, q->hidden);

return TRUE;
}

/******************************************************************\
* SimplifyFileName()
*
* input:
* szFileName - buffer of string to be "simplified"
* rv - (out parameter) - buffer where simplified string
* is to be stored
*
* purpose:
* strips the characters [, ], >, |, and ; from szFileName and
* stores the result in rv.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\******************************************************************/

BOOL SimplifyFileName(LPTSTR szFileName, LPTSTR rv) {
INT i, j;

for (i = 0, j = 0; szFileName[i]; ) {
if (szFileName[i] == TEXT('[') ||
szFileName[i] == TEXT(']') ||
szFileName[i] == TEXT('|') ||
szFileName[i] == TEXT('>') ||
szFileName[i] == TEXT(';'))
i++;
else {
rv[j] = szFileName[i];
i++;
j++;
}
}

rv[j] = TEXT('\0');
return TRUE;
}

/******************************************************************\
* ReplaceEscapeCharacters()
*
* input:
* szFileName - input buffer
* rv - (out parameter) - output buffer
*
* purpose:
* replaces '&' characters in szFileName with '\&' and stores
* the result in rv.
*
* returns:
* TRUE if successful
* FALSE if unsuccessful
\******************************************************************/

BOOL ReplaceEscapeCharacters(LPTSTR szFileName, LPTSTR rv) {
INT i, j;

for (i = 0, j = 0; szFileName[i]; ) {
if (szFileName[i] == TEXT('&')) {
rv[j] = TEXT('&');
rv[j + 1] = TEXT('&');
i++;
j += 2;
}
else {
rv[j] = szFileName[i];
i++;
j++;
}
}

rv[j] = TEXT('\0');
return TRUE;
}

/******************************************************************\
* CFilerlstrcmp: Case-insensitive comparison of the strings s and t.
* foo and |foo are the same.
* [foo] and |[foo] are the same.
* [foo] and foo are not the same.
\******************************************************************/

INT CFilerlstrcmp(LPCTSTR s, LPCTSTR t)
{
int i = 0, j = 0;

if (s[0] == TEXT('|') || s[0] == TEXT('>') || s[0] == TEXT(';'))
i++;

if (t[0] == TEXT('|') || t[0] == TEXT('>') || t[0] == TEXT(';'))
j++;

for ( ; toupper(s[i]) == toupper(t[j]); i++, j++)
if (toupper(s[i]) == TEXT('\0'))
return 0;

return toupper(s[i]) - toupper(t[j]);
}


TABLE.H

// TABLE.H

#ifndef TABLE_INCLUDED
#define TABLE_INCLUDED

typedef struct elem {
LPTSTR displayed;
LPTSTR hidden;
struct elem *next;
} ELEM;

typedef struct table {
ELEM *pCurrentFiles;
INT iNumElems;
} *TABLE;

TABLETableNew(VOID);
BOOL TableFree(TABLE);
BOOL TableAdd(TABLE, LPTSTR, LPTSTR);
BOOL TableInsert(TABLE, LPTSTR, LPTSTR, INT);
BOOLTableRemove(TABLE, INT);
BOOL TableSort(TABLE, INT compare(LPCTSTR, LPCTSTR));
BOOL TableSend(TABLE, HWND);
BOOLTableGetHidden(TABLE, INT, LPTSTR);
BOOLSimplifyFileName(LPTSTR, LPTSTR);
VOID CheckHeap(LPTSTR);
INT CFilerlstrcmp(LPCTSTR, LPCTSTR);
INT GetSize(TABLE);
BOOL ReplaceEscapeCharacters(LPTSTR, LPTSTR);
BOOL TableFind(TABLE, LPTSTR);

#endif


README.TXT

File Encryption

The ENCRYPT sample is a console application that encrypts files. Files
encrypted with this sample can be later decrypted with the DECRYPT sample.

Note that the INITUSER sample (or equivalent) must be run prior to running
these samples, to create a key container for the default user.

Usage
-----

The ENCRYPT sample is run from the command line as follows:

    encrypt <source file> <dest file> [ <password> ]

The <source file> argument specifies the filename of the plaintext file
to be encrypted, and the <dest file> argument specifies the filename of
the ciphertext file to be created. The optional <password> argument specifies
a password with which to encrypt the file.

If no password is specified, then a random session key is used to encrypt
the file. This session key is then encrypted with the key exchange public
key of the default user and stored with the encrypted file. In this case,
the corresponding key exchange private key is later used (by DECRYPT) to
decrypt the session key, which is used in turn to decrypt the file itself.

The DECRYPT sample is run from the command line as follows:

    decrypt <source file> <dest file> [ <password> ]

The <source file> argument specifies the filename of the ciphertext file
to be decrypted, and the <dest file> argument specifies the filename of
the plaintext file to be created. The optional <password> argument specifies
a password with which to decrypt the file.

If a bogus password is supplied to DECRYPT, no error is typically generated.
Of course, the file isn't decrypted properly either.

Exercises for the Reader
------------------------

1. By default, these samples use the RC4 stream cipher to perform the
   encryption and decryption operations. If the USE_BLOCK_CIPHER constant is
   defined at the top of each file, however, the RC2 block cipher is used
   instead.

2. For the sake of simplicity, these samples do not use salt values or (in the
   case of a block cipher) initialization vectors (IVs). This greatly
   diminishes their effective security. It would be a small matter to modify
   these programs such that salt values and IVs are used (see CryptSetKeyParam
   in the documentation). These values should be generated using the
   CryptGenRandom function, and need to be stored (in a non-encrypted form)
   along with the ciphertext file.

3. Another command line argument could be added, which would specify a file
   containing the public key to use when encrypting the session key. This file
   would ideally be in the form of a certificate. If this is done, then it
   becomes possible to encrypt a file such that only the owner of the
   corresponding private key would be able to decrypt the file. This is useful
   if you want to send the file to someone else. Note that this is only
   applicable in the case when a password is not specified.


ENCRYPT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <wincrypt.h>

#ifdef USE_BLOCK_CIPHER
    // defines for RC2 block cipher
    #define ENCRYPT_ALGORITHMCALG_RC2
    #define ENCRYPT_BLOCK_SIZE8
#else
    // defines for RC4 stream cipher
    #define ENCRYPT_ALGORITHMCALG_RC4
    #define ENCRYPT_BLOCK_SIZE1
#endif

static BOOL CAPIEncryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);

/*****************************************************************************/
void _cdecl main(int argc, char *argv[])
{
    PCHAR szSource= NULL;
    PCHAR szDestination = NULL;
    PCHAR szPassword= NULL;

    // Validate argument count.
    if(argc != 3 && argc != 4) {
printf("USAGE: encrypt <source file> <dest file> [ <password> ]\n");
exit(1);
    }

    // Parse arguments.
    szSource   = argv[1];
    szDestination  = argv[2];
    if(argc == 4) {
szPassword = argv[3];
    }

    if(!CAPIEncryptFile(szSource, szDestination, szPassword)) {
printf("Error encrypting file!\n");
exit(1);
    }

    exit(0);
}

/*****************************************************************************/
static BOOL CAPIEncryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword)
{
    FILE *hSource      = NULL;
    FILE *hDestination = NULL;
    INT eof = 0;

    HCRYPTPROV hProv   = 0;
    HCRYPTKEY hKey     = 0;
    HCRYPTKEY hXchgKey = 0;
    HCRYPTHASH hHash   = 0;

    PBYTE pbKeyBlob = NULL;
    DWORD dwKeyBlobLen;

    PBYTE pbBuffer = NULL;
    DWORD dwBlockLen;
    DWORD dwBufferLen;
    DWORD dwCount;

    BOOL status = FALSE;

    // Open source file.
    if((hSource = fopen(szSource,"rb")) == NULL) {
printf("Error opening Plaintext file!\n");
        goto done;
    }

    // Open destination file.
    if((hDestination = fopen(szDestination,"wb")) == NULL) {
printf("Error opening Ciphertext file!\n");
        goto done;
    }

    // Get handle to the default provider.
    if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
        goto done;
    }

    if(szPassword == NULL) {
// Encrypt the file with a random session key.

// Create a random session key.
if(!CryptGenKey(hProv, ENCRYPT_ALGORITHM, CRYPT_EXPORTABLE, &hKey)) {
    printf("Error %x during CryptGenKey!\n", GetLastError());
    goto done;
}

// Get handle to key exchange public key.
if(!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hXchgKey)) {
    printf("Error %x during CryptGetUserKey!\n", GetLastError());
    goto done;
}

// Determine size of the key blob and allocate memory.
if(!CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, NULL, &dwKeyBlobLen)) {
    printf("Error %x computing blob length!\n", GetLastError());
    goto done;
}
if((pbKeyBlob = malloc(dwKeyBlobLen)) == NULL) {
    printf("Out of memory!\n");
    goto done;
}

// Export session key into a simple key blob.
if(!CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, pbKeyBlob, &dwKeyBlobLen)) {
    printf("Error %x during CryptExportKey!\n", GetLastError());
    goto done;
}

// Release key exchange key handle.
CryptDestroyKey(hXchgKey);
hXchgKey = 0;

// Write size of key blob to destination file.
fwrite(&dwKeyBlobLen, sizeof(DWORD), 1, hDestination);
if(ferror(hDestination)) {
    printf("Error writing header!\n");
    goto done;
}

// Write key blob to destination file.
fwrite(pbKeyBlob, 1, dwKeyBlobLen, hDestination);
if(ferror(hDestination)) {
    printf("Error writing header!\n");
    goto done;
}

    } else {
// Encrypt the file with a session key derived from a password.

// Create a hash object.
if(!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
    printf("Error %x during CryptCreateHash!\n", GetLastError());
    goto done;
}

// Hash in the password data.
if(!CryptHashData(hHash, szPassword, strlen(szPassword), 0)) {
    printf("Error %x during CryptHashData!\n", GetLastError());
    goto done;
}

// Derive a session key from the hash object.
if(!CryptDeriveKey(hProv, ENCRYPT_ALGORITHM, hHash, 0, &hKey)) {
    printf("Error %x during CryptDeriveKey!\n", GetLastError());
    goto done;
}

// Destroy the hash object.
CryptDestroyHash(hHash);
hHash = 0;
    }

    // Determine number of bytes to encrypt at a time. This must be a multiple
    // of ENCRYPT_BLOCK_SIZE.
    dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;

    // Determine the block size. If a block cipher is used this must have
    // room for an extra block.
    if(ENCRYPT_BLOCK_SIZE > 1) {
dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
    } else {
dwBufferLen = dwBlockLen;
    }

    // Allocate memory.
    if((pbBuffer = malloc(dwBufferLen)) == NULL) {
printf("Out of memory!\n");
goto done;
    }

    // Encrypt source file and write to Source file.
    do {
// Read up to 'dwBlockLen' bytes from source file.
dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
if(ferror(hSource)) {
    printf("Error reading Plaintext!\n");
            goto done;
        }
eof = feof(hSource);

        // Encrypt data
if(!CryptEncrypt(hKey, 0, eof, 0, pbBuffer, &dwCount, dwBufferLen)) {
            printf("bytes required:%d\n",dwCount);
            printf("Error %x during CryptEncrypt!\n", GetLastError());
            goto done;
        }

// Write data to destination file.
fwrite(pbBuffer, 1, dwCount, hDestination);
if(ferror(hDestination)) {
    printf("Error writing Ciphertext!\n");
            goto done;
        }
    } while(!feof(hSource));

    status = TRUE;

    printf("OK\n");

    done:

    // Close files.
    if(hSource) fclose(hSource);
    if(hDestination) fclose(hDestination);

    // Free memory.
    if(pbKeyBlob) free(pbKeyBlob);
    if(pbBuffer) free(pbBuffer);

    // Destroy session key.
    if(hKey) CryptDestroyKey(hKey);

    // Release key exchange key handle.
    if(hXchgKey) CryptDestroyKey(hXchgKey);

    // Destroy hash object.
    if(hHash) CryptDestroyHash(hHash);

    // Release provider handle.
    if(hProv) CryptReleaseContext(hProv, 0);

    return(status);
}


DECRYPT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <wincrypt.h>

#ifdef USE_BLOCK_CIPHER
    // defines for RC2 block cipher
    #define ENCRYPT_ALGORITHMCALG_RC2
    #define ENCRYPT_BLOCK_SIZE8
#else
    // defines for RC4 stream cipher
    #define ENCRYPT_ALGORITHMCALG_RC4
    #define ENCRYPT_BLOCK_SIZE1
#endif

static BOOL CAPIDecryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);

/*****************************************************************************/
void _cdecl main(int argc, char *argv[])
{
    PCHAR szSource= NULL;
    PCHAR szDestination = NULL;
    PCHAR szPassword= NULL;

    // Validate argument count.
    if(argc != 3 && argc != 4) {
printf("USAGE: decrypt <source file> <dest file> [ <password> ]\n");
exit(1);
    }

    // Parse arguments.
    szSource   = argv[1];
    szDestination  = argv[2];
    if(argc == 4) {
szPassword = argv[3];
    }

    if(!CAPIDecryptFile(szSource, szDestination, szPassword)) {
printf("Error encrypting file!\n");
exit(1);
    }

    exit(0);
}

/*****************************************************************************/
static BOOL CAPIDecryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword)
{
    FILE *hSource      = NULL;
    FILE *hDestination = NULL;
    INT eof = 0;

    HCRYPTPROV hProv   = 0;
    HCRYPTKEY hKey     = 0;
    HCRYPTHASH hHash   = 0;

    PBYTE pbKeyBlob = NULL;
    DWORD dwKeyBlobLen;

    PBYTE pbBuffer = NULL;
    DWORD dwBlockLen;
    DWORD dwBufferLen;
    DWORD dwCount;

    BOOL status = FALSE;

    // Open source file.
    if((hSource = fopen(szSource,"rb")) == NULL) {
printf("Error opening Ciphertext file!\n");
        goto done;
    }

    // Open destination file.
    if((hDestination = fopen(szDestination,"wb")) == NULL) {
printf("Error opening Plaintext file!\n");
        goto done;
    }

    // Get handle to the default provider.
    if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
        goto done;
    }

    if(szPassword == NULL) {
// Decrypt the file with the saved session key.

// Read key blob length from source file and allocate memory.
fread(&dwKeyBlobLen, sizeof(DWORD), 1, hSource);
if(ferror(hSource) || feof(hSource)) {
    printf("Error reading file header!\n");
    goto done;
}
if((pbKeyBlob = malloc(dwKeyBlobLen)) == NULL) {
    printf("Out of memory or improperly formatted source file!\n");
    goto done;
}

// Read key blob from source file.
fread(pbKeyBlob, 1, dwKeyBlobLen, hSource);
if(ferror(hSource) || feof(hSource)) {
    printf("Error reading file header!\n");
    goto done;
}

// Import key blob into CSP.
if(!CryptImportKey(hProv, pbKeyBlob, dwKeyBlobLen, 0, 0, &hKey)) {
    printf("Error %x during CryptImportKey!\n", GetLastError());
    goto done;
}
    } else {
// Decrypt the file with a session key derived from a password.

// Create a hash object.
if(!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
    printf("Error %x during CryptCreateHash!\n", GetLastError());
    goto done;
}

// Hash in the password data.
if(!CryptHashData(hHash, szPassword, strlen(szPassword), 0)) {
    printf("Error %x during CryptHashData!\n", GetLastError());
    goto done;
}

// Derive a session key from the hash object.
if(!CryptDeriveKey(hProv, ENCRYPT_ALGORITHM, hHash, 0, &hKey)) {
    printf("Error %x during CryptDeriveKey!\n", GetLastError());
    goto done;
}

// Destroy the hash object.
CryptDestroyHash(hHash);
hHash = 0;
    }

    // Determine number of bytes to decrypt at a time. This must be a multiple
    // of ENCRYPT_BLOCK_SIZE.
    dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
    dwBufferLen = dwBlockLen;

    // Allocate memory.
    if((pbBuffer = malloc(dwBufferLen)) == NULL) {
printf("Out of memory!\n");
goto done;
    }

    // Decrypt source file and write to destination file.
    do {
// Read up to 'dwBlockLen' bytes from source file.
dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
if(ferror(hSource)) {
    printf("Error reading Ciphertext!\n");
            goto done;
        }
eof = feof(hSource);

        // Decrypt data
        if(!CryptDecrypt(hKey, 0, eof, 0, pbBuffer, &dwCount)) {
            printf("Error %x during CryptDecrypt!\n", GetLastError());
            goto done;
        }

        // Write data to destination file.
fwrite(pbBuffer, 1, dwCount, hDestination);
if(ferror(hDestination)) {
    printf("Error writing Plaintext!\n");
            goto done;
        }
    } while(!feof(hSource));

    status = TRUE;

    printf("OK\n");

    done:

    // Close files.
    if(hSource) fclose(hSource);
    if(hDestination) fclose(hDestination);

    // Free memory.
    if(pbKeyBlob) free(pbKeyBlob);
    if(pbBuffer) free(pbBuffer);

    // Destroy session key.
    if(hKey) CryptDestroyKey(hKey);

    // Destroy hash object.
    if(hHash) CryptDestroyHash(hHash);

    // Release provider handle.
    if(hProv) CryptReleaseContext(hProv, 0);

    return(status);
}


README.TXT

Enumerate Algorithms

The ENUMALGS sample is a console application that lists the algorithms
supported by the default PROV_RSA_FULL provider. By default, this will
be the Microsoft RSA Base Provider, which is included along with the
operating system.

Note that the INITUSER sample (or equivalent) must be run prior to running
these samples, in order to create a key container for the default user.

In addition to listing the name of each supported algorithm, this sample
also lists:

  - The type of algorithm (encryption, hash, key exchange, or signature).

  - The key length used by the algorithm (or number of bits in the
    hash value for hash algorithms).

  - The algorithm identifier for the algorithm. This value can be passed
    to the appropriate CryptoAPI function in order to create a key or
    hash object that makes use of the particular algorithm.

The guts of this sample can be used by applications that want to display
a list of available applications to the user.


ENUMALGS.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <windows.h>
#include <wincrypt.h>

/*****************************************************************************/
void _cdecl main(int argc, char *argv[])
{
    HCRYPTPROV hProv = 0;
    BYTE *ptr = NULL;
    DWORD i;
    ALG_ID aiAlgid;
    DWORD dwBits;
    DWORD dwNameLen;
    CHAR szName[100];      // Often allocated dynamically.
    BYTE pbData[1000];      // Often allocated dynamically.
    DWORD dwDataLen;
    DWORD dwFlags;
    CHAR *pszAlgType = NULL;

    // Get handle to the default provider.
    if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
        goto done;
    }

    // Enumerate the supported algorithms.
    for(i=0 ; ; i++) {
// Set the CRYPT_FIRST flag the first time through the loop.
if(i == 0) {
    dwFlags = CRYPT_FIRST;
} else {
    dwFlags = 0;
}

// Retrieve information about an algorithm.
dwDataLen = 1000;
if(!CryptGetProvParam(hProv, PP_ENUMALGS, pbData, &dwDataLen, 0)) {
    if(GetLastError() == ERROR_NO_MORE_ITEMS) {
// Exit the loop.
break;
    } else {
printf("Error %x reading algorithm!\n", GetLastError());
return;
    }
}

// Extract algorithm information from 'pbData' buffer.
ptr = pbData;
aiAlgid = *(ALG_ID *)ptr;
ptr += sizeof(ALG_ID);
dwBits = *(DWORD *)ptr;
ptr += sizeof(DWORD);
dwNameLen = *(DWORD *)ptr;
ptr += sizeof(DWORD);
strncpy(szName, ptr,dwNameLen);

// Determine algorithm type.
switch(GET_ALG_CLASS(aiAlgid)) {
    case ALG_CLASS_DATA_ENCRYPT: pszAlgType = "Encrypt  ";
 break;
    case ALG_CLASS_HASH: pszAlgType = "Hash     ";
 break;
    case ALG_CLASS_KEY_EXCHANGE: pszAlgType = "Exchange ";
 break;
    case ALG_CLASS_SIGNATURE: pszAlgType = "Signature";
 break;
    default: pszAlgType = "Unknown  ";
}

// Print information about algorithm.
printf("Name:%-14s Type:%s  Bits:%-4d Algid:%8.8xh\n",
    szName, pszAlgType, dwBits, aiAlgid
);
    }

    done:

    // Release CSP handle.
    if(hProv) CryptReleaseContext(hProv,0);
}


README.TXT

Initialize Client

The INITUSER sample sets up the default cryptographic client. This sample
(or its equivalent) must be run prior to running the other cryptography
samples, so that they will work correctly.

The following operations are performed:

1. A default key container is created.
2. A digital signature key pair is created within the key container.
3. A key exchange key pair is created within the key container.

These operations only need to be done once, unless the operating system
is reinstalled. If the default key container and key pairs have already
been created, this sample performs no operations.

The INITUSER sample uses the following CryptoAPI functions:

    CryptAcquireContext
    CryptGetProvParam
    CryptGenKey
    CryptGetUserKey
    CryptDestroyKey
    CryptReleaseContext


INITUSER.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <wincrypt.h>

/*****************************************************************************/
void _cdecl main(void)
{
    HCRYPTPROV hProv;
    HCRYPTKEY hKey;
    CHAR szUserName[100];
    DWORD dwUserNameLen = 100;

    // Attempt to acquire a handle to the default key container.
    if(!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, 0)) {
// Some sort of error occured.

// Create default key container.
if(!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
    printf("Error creating key container!\n");
    exit(1);
}

// Get name of default key container.
if(!CryptGetProvParam(hProv, PP_CONTAINER, szUserName, &dwUserNameLen, 0)) {
    // Error getting key container name.
    szUserName[0] = 0;
}

printf("Create key container '%s'\n",szUserName);
    }

    // Attempt to get handle to signature key.
    if(!CryptGetUserKey(hProv, AT_SIGNATURE, &hKey)) {
if(GetLastError() == NTE_NO_KEY) {
    // Create signature key pair.
    printf("Create signature key pair\n");

    if(!CryptGenKey(hProv,AT_SIGNATURE,0,&hKey)) {
printf("Error %x during CryptGenKey!\n", GetLastError());
exit(1);
    } else {
CryptDestroyKey(hKey);
    }
} else {
    printf("Error %x during CryptGetUserKey!\n", GetLastError());
    exit(1);
}
    }

    // Attempt to get handle to exchange key.
    if(!CryptGetUserKey(hProv,AT_KEYEXCHANGE,&hKey)) {
if(GetLastError()==NTE_NO_KEY) {
    // Create key exchange key pair.
    printf("Create key exchange key pair\n");

    if(!CryptGenKey(hProv,AT_KEYEXCHANGE,0,&hKey)) {
printf("Error %x during CryptGenKey!\n", GetLastError());
exit(1);
    } else {
CryptDestroyKey(hKey);
    }
} else {
    printf("Error %x during CryptGetUserKey!\n", GetLastError());
    exit(1);
}
    }

    CryptReleaseContext(hProv,0);

    printf("OK\n");

    exit(0);
}


README.TXT

File Signature

The SIGNFILE sample is a console application that signs files. Files
signed with this sample can be later verified with the VERIFILE sample.

Note that the INITUSER sample (or equivalent) must be run prior to running
these samples, in order to create a key container for the default user.

Usage
-----

The SIGNFILE sample is run from the command line as follows:

    signfile <source file> <signature file> <description>

The <source file> argument specifies the filename of the file to be signed,
and the <signature file> argument specifies the filename of the file in which
to place the signature data. The <description> argument specifies a textual
description of the data being signed. This can consist of empty quotes ("")
if no description is required. See CryptSignHash in the online documentation
for more information on signatures and description strings.

The VERIFILE sample is run with the same arguments as SIGNFILE. If the
contents of the source file, signature file, or description string has
changed in any way from when the file was originally signed, the program
will fail.

Exercises for the Reader
------------------------

1. These samples would be more useful if the signature public key was stored
   in the signature file along with the signature data. This public key would
   need to be wrapped in a certificate (also known as a credential) to protect
   it from unauthorized modification. If this were to be done, then the data
   file and its signature could be verified by anyone, even if the original
   key pair used to produce the signature is destroyed.

2. The SIGNFILE sample could be easily modified such that the description data
   is stored in the signature file (in a non-encrypted format) along with the
   signature data. The VERIFILE sample could then read the description from
   the signature file, instead of requiring it as a command line argument.


SIGNFILE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <wincrypt.h>

static BOOL SignFile(PCHAR szSourceFile, PCHAR szSignatureFile, PCHAR szDescription);

/*****************************************************************************/
void _cdecl main(int argc, char *argv[])
{
    PCHAR szSourceFile  = NULL;
    PCHAR szSignatureFile = NULL;
    PCHAR szDescription   = NULL;

    // Validate argument count.
    if(argc != 4) {
printf("USAGE: signfile <source file> <signature file> <description>\n");
exit(1);
    }

    // Parse arguments.
    szSourceFile    = argv[1];
    szSignatureFile = argv[2];
    szDescription   = argv[3];

    if(!SignFile(szSourceFile, szSignatureFile, szDescription)) {
printf("Error signing file!\n");
exit(1);
    }

    exit(0);
}

/*****************************************************************************/
static BOOL SignFile(PCHAR szSourceFile, PCHAR szSignatureFile, PCHAR szDescription)
{
    FILE *hSourceFile = NULL;
    FILE *hSignatureFile = NULL;
    INT eof = 0;

    HCRYPTPROV hProv = 0;
    HCRYPTHASH hHash = 0;

    PBYTE pbSignature = NULL;
    DWORD dwSignatureLen;

    PBYTE pbBuffer    = NULL;
    DWORD dwBufferLen;
    DWORD dwCount;

    // Get handle to the default provider.
    if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
goto error;
    }

    //
    // Hash source file.
    //

    // Determine number of bytes to hash at once.
    dwBufferLen = 1000;

    // Allocate memory for 'pbBuffer'.
    if((pbBuffer = malloc(dwBufferLen)) == NULL) {
printf("Out of memory 1!\n");
goto error;
    }

    // Open source file.
    if((hSourceFile = fopen(szSourceFile,"rb")) == NULL) {
printf("Error opening source file!\n");
goto error;
    }

    // Create a hash object.
    if(!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
printf("Error %x during CryptCreateHash!\n", GetLastError());
goto error;
    }

    // Hash source file.
    do {
// Read up to 'dwBufferLen' bytes from source file.
dwCount = fread(pbBuffer, 1, dwBufferLen, hSourceFile);
if(ferror(hSourceFile)) {
    printf("Error reading Plaintext!\n");
    goto error;
        }
eof = feof(hSourceFile);

// Add data to hash object.
if(!CryptHashData(hHash, pbBuffer, dwCount, 0)) {
    printf("Error %x during CryptHashData!\n", GetLastError());
    goto error;
}
    } while(!feof(hSourceFile));

    // Close source file.
    fclose(hSourceFile);
    hSourceFile = 0;

    // Free 'pbBuffer' memory.
    free(pbBuffer);
    pbBuffer = NULL;

    //
    // Sign hash object.
    //

    // Determine size of signature.
    dwCount = 0;
    if(!CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, NULL, &dwSignatureLen)) {
printf("Error %x during CryptSignHash 1!\n", GetLastError());
goto error;
    }

    // Allocate memory for 'pbSignature'.
    if((pbSignature = malloc(dwSignatureLen)) == NULL) {
printf("Out of memory 2!\n");
goto error;
    }

    // Sign hash object (with signature key).
    if(!CryptSignHash(hHash, AT_SIGNATURE, szDescription, 0, pbSignature, &dwSignatureLen)) {
printf("Error %x during CryptSignHash 2!\n", GetLastError());
goto error;
    }

    //
    // Write signature to signature file.
    //

    // Open signature file.
    if((hSignatureFile = fopen(szSignatureFile,"wb")) == NULL) {
printf("Error opening signature file!\n");
goto error;
    }

    // Write signature to signature file.
    fwrite(pbSignature, 1, dwSignatureLen, hSignatureFile);
    if(ferror(hSignatureFile)) {
printf("Error writing signature!\n");
goto error;
    }

    // Close signature file.
    fclose(hSignatureFile);
    hSignatureFile = 0;

    // Free 'pbSignature' memory.
    free(pbSignature);
    pbSignature = NULL;

    // Destroy hash object.
    CryptDestroyHash(hHash);
    hHash = 0;

    // Release provider handle.
    CryptReleaseContext(hProv, 0);
    hProv = 0;

    printf("OK\n");

    return TRUE;

    error:

    //
    // Do cleanup
    //

    // Close files.
    if(hSourceFile) fclose(hSourceFile);
    if(hSignatureFile) fclose(hSignatureFile);

    // Free memory.
    if(pbSignature) free(pbSignature);
    if(pbBuffer) free(pbBuffer);

    // Release crypto handles.
    if(hHash) CryptDestroyHash(hHash);
    if(hProv) CryptReleaseContext(hProv, 0);

    return FALSE;
}


VERIFILE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1996 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <wincrypt.h>

static BOOL VerifyFile(PCHAR szSourceFile, PCHAR szSignatureFile, PCHAR szDescription);

/*****************************************************************************/
void _cdecl main(int argc, char *argv[])
{
    PCHAR szSourceFile  = NULL;
    PCHAR szSignatureFile = NULL;
    PCHAR szDescription  = NULL;

    // Validate argument count.
    if(argc != 4) {
printf("USAGE: signfile <source file> <signature file> <description>\n");
exit(1);
    }

    // Parse arguments.
    szSourceFile    = argv[1];
    szSignatureFile = argv[2];
    szDescription   = argv[3];

    if(!VerifyFile(szSourceFile, szSignatureFile, szDescription)) {
printf("Error verifying file!\n");
exit(1);
    }

    exit(0);
}

/*****************************************************************************/
static BOOL VerifyFile(PCHAR szSourceFile, PCHAR szSignatureFile, PCHAR szDescription)
{
    FILE *hSourceFile = NULL;
    FILE *hSignatureFile = NULL;
    INT eof = 0;

    HCRYPTPROV hProv = 0;
    HCRYPTHASH hHash = 0;
    HCRYPTKEY hSigPublicKey = 0;

    PBYTE pbSignature = NULL;
    DWORD dwSignatureLen;

    PBYTE pbBuffer = NULL;
    DWORD dwBufferLen;
    DWORD dwCount;

    // Get handle to the default provider.
    if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
goto error;
    }

    //
    // Hash source file.
    //

    // Determine number of bytes to hash at once.
    dwBufferLen = 1000;

    // Allocate memory for 'pbBuffer'.
    if((pbBuffer = malloc(dwBufferLen)) == NULL) {
printf("Out of memory 1!\n");
goto error;
    }

    // Open source file.
    if((hSourceFile = fopen(szSourceFile,"rb")) == NULL) {
printf("Error opening source file!\n");
goto error;
    }

    // Create a hash object.
    if(!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
printf("Error %x during CryptCreateHash!\n", GetLastError());
goto error;
    }

    // Hash source file.
    do {
// Read up to 'dwBufferLen' bytes from source file.
dwCount = fread(pbBuffer, 1, dwBufferLen, hSourceFile);
if(ferror(hSourceFile)) {
    printf("Error reading Plaintext!\n");
    goto error;
        }
eof = feof(hSourceFile);

// Add data to hash object.
if(!CryptHashData(hHash, pbBuffer, dwCount, 0)) {
    printf("Error %x during CryptHashData!\n", GetLastError());
    goto error;
}
    } while(!feof(hSourceFile));

    // Close source file.
    fclose(hSourceFile);
    hSourceFile = 0;

    // Free 'pbBuffer' memory.
    free(pbBuffer);
    pbBuffer = NULL;

    //
    // Read in signature file.
    //

    // Determine size of signature.
    dwSignatureLen = 0;
    if(!CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, NULL, &dwSignatureLen)) {
printf("Error %x during CryptSignHash!\n", GetLastError());
goto error;
    }

    // Allocate memory for 'pbSignature'.
    if((pbSignature = malloc(dwSignatureLen)) == NULL) {
printf("Out of memory 2!\n");
goto error;
    }

    // Open signature file.
    if((hSignatureFile = fopen(szSignatureFile,"rb")) == NULL) {
printf("Error opening signature file!\n");
goto error;
    }

    // Read signature data.
    dwCount = fread(pbSignature, 1, dwSignatureLen, hSignatureFile);
    if(ferror(hSignatureFile)) {
printf("Error reading signature!\n");
goto error;
    }

    // Close signature file.
    fclose(hSignatureFile);
    hSignatureFile = 0;

    //
    // Verify signature.
    //

    // Get handle to signature key.
    if(!CryptGetUserKey(hProv, AT_SIGNATURE, &hSigPublicKey)) {
printf("Error %x during CryptGetUserKey!\n", GetLastError());
goto error;
    }

    // Verify signature.
    if(!CryptVerifySignature(hHash, pbSignature, dwSignatureLen, hSigPublicKey, szDescription, 0)) {
if(GetLastError() == NTE_BAD_SIGNATURE) {
    printf("Signature did not match!\n");
} else {
    printf("Error %x during CryptVerifySignature!\n", GetLastError());
}
goto error;
    }

    // Release signature key.
    CryptDestroyKey(hSigPublicKey);
    hSigPublicKey = 0;

    // Free 'pbSignature' memory.
    free(pbSignature);
    pbSignature = NULL;

    // Destroy hash object.
    CryptDestroyHash(hHash);
    hHash = 0;

    // Release provider handle.
    CryptReleaseContext(hProv, 0);
    hProv = 0;

    printf("OK\n");

    return TRUE;

    error:

    //
    // Do cleanup
    //

    // Close files.
    if(hSourceFile) fclose(hSourceFile);
    if(hSignatureFile) fclose(hSignatureFile);

    // Free memory.
    if(pbSignature) free(pbSignature);
    if(pbBuffer) free(pbBuffer);

    // Release crypto handles.
    if(hHash) CryptDestroyHash(hHash);
    if(hSigPublicKey) CryptDestroyKey(hSigPublicKey);
    if(hProv) CryptReleaseContext(hProv, 0);

    return FALSE;
}


README.TXT

This module illustrates new Acl management API for Windows NT 4.0.

Developers of new software which is to run on Windows NT version 4.0
and above are encouraged to utilize these security API rather than
implementing code which uses lower level security API.  The development
and test time can be drastically reduced by utilizing these new API.

This sample illustrates this point by implementing code which modifies
the security on an existing file using the new Windows NT 4.0
Acl management API.

The following new API functions are illustrated in this sample:

GetNamedSecurityInfo()
BuildExplicitAccessWithName()
SetEntriesInAcl()
SetNamedSecurityInfo()

The following lower-level security API would have been used to achieve
the same result:

LookupAccountName()
InitializeSecurityDescriptor()
InitializeAcl()
GetSecurityDescriptorDacl()
GetAclInformation()
GetAce()
SetSecurityDescriptorDacl()
AddAce()
AddAccessAllowedAce() / AddAccessDeniedAce()
GetFileSecurity()
SetFileSecurity()

Less code and less complex code is required to achieve this task using
the new Windows NT 4.0 Acl management API.


ACLAPI.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    aclapi.c

Abstract:

    This module illustrates new Acl management API for Windows NT 4.0.

    Developers of new software which is to run on Windows NT version 4.0
    and above are encouraged to utilize these security API rather than
    implementing code which uses lower level security API.  The development
    and test time can be drastically reduced by utilizing these new API.

    This sample illustrates this point by implementing code which modifies
    the security on an existing file using the new Windows NT 4.0
    Acl management API.

    The following new API functions are illustrated in this sample:

    GetNamedSecurityInfo()
    BuildExplicitAccessWithName()
    SetEntriesInAcl()
    SetNamedSecurityInfo()

    The following lower-level security API would have been used to achieve
    the same result:

    LookupAccountName()
    InitializeSecurityDescriptor()
    InitializeAcl()
    GetSecurityDescriptorDacl()
    GetAclInformation()
    GetAce()
    SetSecurityDescriptorDacl()
    AddAce()
    AddAccessAllowedAce() / AddAccessDeniedAce()
    GetFileSecurity()
    SetFileSecurity()

    Less code and less complex code is required to achieve this task using
    the new Windows NT 4.0 Acl management API.

Author:

    Scott Field (sfield)    02-Jun-96

--*/

#include <windows.h>
#include <aclapi.h>
#include <lmerr.h>

#include <stdio.h>

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

void
DisplayLastError(
    LPSTR szAPI
    );

int
__cdecl
main(
    int argc,
    char *argv[]
    )
{
    LPTSTR FileName;
    LPTSTR TrusteeName;

    DWORD AccessMask = GENERIC_ALL;
    DWORD InheritFlag = NO_INHERITANCE;
    ACCESS_MODE option;
    EXPLICIT_ACCESS explicitaccess;

    PACL ExistingDacl;
    PACL NewAcl = NULL;
    PSECURITY_DESCRIPTOR psd = NULL;

    DWORD dwError;
    BOOL bSuccess = FALSE; // assume failure

    if(argc < 4) {
        printf("Usage: %s <filename> {/Deny | /Grant | /Revoke | /Set} [<trustee>] [<permissions>] [<InheritFlag>]\n", argv[0]);
        return RTN_USAGE;
    }

    FileName = argv[1];
    TrusteeName = argv[3];

    if ( (0 == stricmp(argv[2], "/Deny") ) ||
        (0 == stricmp(argv[2], "/D") ) )
    {
      option = DENY_ACCESS;
    } else if ( ( (0 == stricmp(argv[2], "/Revoke") ) ||
                 (0 == stricmp(argv[2], "/R") ) ) )
    {
      option = REVOKE_ACCESS;
    } else if ( (0 == stricmp(argv[2], "/Set") ) ||
               (0 == stricmp(argv[2], "/S") ) )
    {
      option = SET_ACCESS;
    } else if ( (0 == stricmp(argv[2], "/Grant") ) ||
               (0 == stricmp(argv[2], "/G") ) )
    {
      option = GRANT_ACCESS;
    } else {
        printf("Invalid action specified\n");
        return RTN_ERROR;
    }

    if (argc > 4)
    {
        AccessMask = atol( argv[4] );
    }

    if (argc > 5)
    {
       InheritFlag = atol( argv[5] );
    }

    //
    // get current Dacl on specified file
    //

    dwError = GetNamedSecurityInfo(
                        FileName,
                        SE_FILE_OBJECT,
                        DACL_SECURITY_INFORMATION,
                        NULL,
                        NULL,
                        &ExistingDacl,
                        NULL,
                        &psd
                        );

    if(dwError != ERROR_SUCCESS) {
        DisplayLastError("GetNamedSecurityInfo");
        return RTN_ERROR;
    }

    BuildExplicitAccessWithName(
            &explicitaccess,
            TrusteeName,
            AccessMask,
            option,
            InheritFlag
            );

    //
    // add specified access to the object
    //

    dwError = SetEntriesInAcl(
            1,
            &explicitaccess,
            ExistingDacl,
            &NewAcl
            );

    if(dwError != ERROR_SUCCESS) {
        DisplayLastError("SetEntriesInAcl");
        goto cleanup;
    }

    //
    // apply new security to file
    //

    dwError = SetNamedSecurityInfo(
                    FileName,
                    SE_FILE_OBJECT, // object type
                    DACL_SECURITY_INFORMATION,
                    NULL,
                    NULL,
                    NewAcl,
                    NULL
                    );

    if(dwError != ERROR_SUCCESS) {
        DisplayLastError("SetNamedSecurityInfo");
        goto cleanup;
    }

    bSuccess = TRUE; // indicate success

cleanup:

    if( NewAcl != NULL ) AccFree( NewAcl );
    if( psd != NULL) AccFree( psd );

    if(!bSuccess)
        return RTN_ERROR;

    return RTN_OK;
}

void
DisplayLastError(
    LPSTR szAPI
    )
{
    HMODULE hModule = NULL; // default to system source
    DWORD dwLastError = GetLastError();
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_IGNORE_INSERTS |
        FORMAT_MESSAGE_FROM_SYSTEM ;

    //
    // if dwLastError is in the network range, load the message source
    //

    if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
        hModule = LoadLibraryEx(
            TEXT("netmsg.dll"),
            NULL,
            LOAD_LIBRARY_AS_DATAFILE
            );

        if(hModule != NULL)
            dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
    }

    printf("%s error! (rc=%lu)\n", szAPI, dwLastError);

    //
    // call FormatMessage() to allow for message text to be acquired
    // from the system or the supplied module handle
    //

    if(dwBufferLength = FormatMessageA(
        dwFormatFlags,
        hModule, // module to get message from (NULL == system)
        dwLastError,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
        (LPSTR) &MessageBuffer,
        0,
        NULL
        ))
    {
        DWORD dwBytesWritten;

        //
        // Output message string on stderr
        //
        WriteFile(
            GetStdHandle(STD_ERROR_HANDLE),
            MessageBuffer,
            dwBufferLength,
            &dwBytesWritten,
            NULL
            );

        //
        // free the buffer allocated by the system
        //
        LocalFree(MessageBuffer);
    }

    //
    // if we loaded a message source, unload it
    //
    if(hModule != NULL)
        FreeLibrary(hModule);
}


AUDIT.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    audit.c

Abstract:

    This module illustrates how to use the Windows NT LSA security API
    to manage the audit status on the local machine or a remote machine.

    Querying the current audit status is illustrated, in addition to
    changing the audit state of an audit event type.  Enabling and
    disabling all auditing is also illustrated.

    When targetting a domain controller for an audit update operation,
    be sure to target the primary domain controller for the domain.
    The audit settings are replicated by the primary domain controller
    to each backup domain controller as appropriate.  The NetGetDCName()
    Lan Manager API call can be used to get the primary domain controller
    computer name from a domain name.

    This sample will target the local machine if no command line argument is
    specified, or the machine specified on argv[1], eg. audit.exe \\winbase

    The sample relies on the ntsecapi.h header file found in the Win32SDK
    \mstools\security directory.

Author:

    Scott Field (sfield)    13-Apr-96

--*/

#include <windows.h>
#include <stdio.h>

#include "ntsecapi.h" // \mstools\samples\win32\winnt\security\include\ntsecapi.h

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

//
// if you have the ddk, include ntstatus.h
//
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS              ((NTSTATUS)0x00000000L)
#define STATUS_INVALID_PARAMETER    ((NTSTATUS)0xC000000DL)
#endif

NTSTATUS
DisplayAudit(
    LSA_HANDLE PolicyHandle
    );

void
DisplayAuditEventOption(
    DWORD EventTypeIndex,
    POLICY_AUDIT_EVENT_OPTIONS EventOption
    );

NTSTATUS
SetAuditEvent(
    LSA_HANDLE PolicyHandle,
    POLICY_AUDIT_EVENT_TYPE EventType,
    POLICY_AUDIT_EVENT_OPTIONS EventOption
    );

NTSTATUS
SetAuditMode(
    LSA_HANDLE PolicyHandle,
    BOOL bEnable
    );

//
// helper functions
//

NTSTATUS
OpenPolicy(
    LPWSTR ServerName,
    DWORD DesiredAccess,
    PLSA_HANDLE PolicyHandle
    );

void
InitLsaString(
    PLSA_UNICODE_STRING LsaString,
    LPWSTR String
    );

void
DisplayNtStatus(
    LPSTR szAPI,        // pointer to Ansi function name
    NTSTATUS Status     // NTSTATUS error value
    );

void
DisplayWinError(
    LPSTR szAPI,    // pointer to Ansi function name
    DWORD dwError   // DWORD WinError
    );

//
// unicode entry point and argv
//
int
__cdecl
wmain(
    int argc,
    wchar_t *argv[]
    )
{
    LPWSTR wComputerName;
    LSA_HANDLE PolicyHandle;
    NTSTATUS Status;

    //
    // pickup machine name if appropriate
    //
    if(argc == 2)
        wComputerName = argv[1];
    else
        wComputerName = NULL; // local machine

    //
    // display current audit state
    //

    Status = OpenPolicy(
                wComputerName,
                POLICY_VIEW_AUDIT_INFORMATION,
                &PolicyHandle
                );

    if(Status == STATUS_SUCCESS) {
        //
        // display current auditing status
        //
        Status = DisplayAudit(PolicyHandle);

        LsaClose(PolicyHandle);

        if(Status != STATUS_SUCCESS) {
            DisplayNtStatus("DisplayAudit", Status);
            return RTN_ERROR;
        }
    } else {
        DisplayNtStatus("OpenPolicy", Status);
        return RTN_ERROR;
    }

    //
    // enable success and failure audits of logon/logoff events
    //

    Status = OpenPolicy(
                wComputerName,
                POLICY_VIEW_AUDIT_INFORMATION |
                POLICY_SET_AUDIT_REQUIREMENTS,
                &PolicyHandle
                );

    if(Status == STATUS_SUCCESS) {

        //
        // enable success and failure auditing of logon/logoff
        //
        Status = SetAuditEvent(
            PolicyHandle,
            AuditCategoryLogon,
            POLICY_AUDIT_EVENT_SUCCESS | POLICY_AUDIT_EVENT_FAILURE
            );

        //
        // enable audits
        //
        if( Status == STATUS_SUCCESS )
            Status = SetAuditMode(PolicyHandle, TRUE);

        LsaClose(PolicyHandle);

        if(Status != STATUS_SUCCESS) {
            DisplayNtStatus("SetAuditMode", Status);
            return RTN_ERROR;
        }
    } else {
        DisplayNtStatus("OpenPolicy", Status);
        return RTN_ERROR;
    }

    return RTN_OK;
}

NTSTATUS
DisplayAudit(
    LSA_HANDLE PolicyHandle
    )
{
    PPOLICY_AUDIT_EVENTS_INFO AuditEvents;
    NTSTATUS Status;
    DWORD i; // index into EventAuditingOptions

    //
    // obtain AuditEvents
    //
    Status = LsaQueryInformationPolicy(
                PolicyHandle,
                PolicyAuditEventsInformation,
                &AuditEvents
                );

    if(Status != STATUS_SUCCESS) return Status;

    //
    // successfully obtained AuditEventsInformation.  Now display.
    //
    if(AuditEvents->AuditingMode) {
        printf("Auditing Enabled\n");
    } else {
        printf("Auditing Disabled\n");
    }

    for(i = 0 ; i < AuditEvents->MaximumAuditEventCount ; i++) {
        DisplayAuditEventOption(i, AuditEvents->EventAuditingOptions[i]);
    }

    //
    // free allocated memory
    //
    LsaFreeMemory(AuditEvents);

    return Status;
}

void
DisplayAuditEventOption(
    DWORD EventTypeIndex,
    POLICY_AUDIT_EVENT_OPTIONS EventOption
    )
{
    printf("AuditCategory");

    switch (EventTypeIndex) {
        case AuditCategorySystem:
        printf("System");
        break;

        case AuditCategoryLogon:
        printf("Logon");
        break;

        case AuditCategoryObjectAccess:
        printf("ObjectAccess");
        break;

        case AuditCategoryPrivilegeUse:
        printf("PrivilegeUse");
        break;

        case AuditCategoryDetailedTracking:
        printf("DetailedTracking");
        break;

        case AuditCategoryPolicyChange:
        printf("PolicyChange");
        break;

        case AuditCategoryAccountManagement:
        printf("AccountManagement");
        break;

        default:
        printf("Unknown");
    }

    if(EventOption & POLICY_AUDIT_EVENT_SUCCESS)
        printf(" AUDIT_EVENT_SUCCESS");

    if(EventOption & POLICY_AUDIT_EVENT_FAILURE)
        printf(" AUDIT_EVENT_FAILURE");

    printf("\n");
}

NTSTATUS
SetAuditEvent(
    LSA_HANDLE PolicyHandle,
    POLICY_AUDIT_EVENT_TYPE EventType,
    POLICY_AUDIT_EVENT_OPTIONS EventOption
    )
{
    PPOLICY_AUDIT_EVENTS_INFO pae;
    NTSTATUS Status;
    DWORD i; // index into EventAuditingOptions

    //
    // obtain AuditEvents
    //
    Status = LsaQueryInformationPolicy(
                PolicyHandle,
                PolicyAuditEventsInformation,
                &pae
                );

    if(Status != STATUS_SUCCESS) return Status;

    //
    // insure we were passed a valid EventType and EventOption
    //
    if((ULONG)EventType > pae->MaximumAuditEventCount ||
      (!EventOption & POLICY_AUDIT_EVENT_MASK) ) {
        LsaFreeMemory(pae);
        return STATUS_INVALID_PARAMETER;
    }

    //
    // set all auditevents to the unchanged status...
    //
    for(i = 0 ; i < pae->MaximumAuditEventCount ; i++) {
        pae->EventAuditingOptions[i] = POLICY_AUDIT_EVENT_UNCHANGED;
    }

    //
    // ...and update only the specified EventType
    //
    pae->EventAuditingOptions[EventType] = EventOption;

    //
    // set the new AuditEvents
    //
    Status = LsaSetInformationPolicy(
                PolicyHandle,
                PolicyAuditEventsInformation,
                pae
                );

    //
    // free allocated memory
    //
    LsaFreeMemory(pae);

    return Status;
}

NTSTATUS
SetAuditMode(
    LSA_HANDLE PolicyHandle,
    BOOL bEnable
    )
{
    PPOLICY_AUDIT_EVENTS_INFO AuditEvents;
    NTSTATUS Status;
    DWORD i;

    //
    // obtain current AuditEvents
    //
    Status = LsaQueryInformationPolicy(
                PolicyHandle,
                PolicyAuditEventsInformation,
                &AuditEvents
                );

    if(Status != STATUS_SUCCESS) return Status;

    //
    // update the relevant member
    //
    AuditEvents->AuditingMode = bEnable;

    //
    // set all auditevents to the unchanged status...
    //
    for(i = 0 ; i < AuditEvents->MaximumAuditEventCount ; i++) {
        AuditEvents->EventAuditingOptions[i] = POLICY_AUDIT_EVENT_UNCHANGED;
    }

    //
    // set the new auditing mode (enabled or disabled)
    //
    Status = LsaSetInformationPolicy(
                PolicyHandle,
                PolicyAuditEventsInformation,
                AuditEvents
                );

    LsaFreeMemory(AuditEvents);

    return Status;
}

void
InitLsaString(
    PLSA_UNICODE_STRING LsaString,
    LPWSTR String
    )
{
    DWORD StringLength;

    if(String == NULL) {
        LsaString->Buffer = NULL;
        LsaString->Length = 0;
        LsaString->MaximumLength = 0;

        return;
    }

    StringLength = lstrlenW(String);
    LsaString->Buffer = String;
    LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
    LsaString->MaximumLength = (USHORT) (StringLength + 1) *
        sizeof(WCHAR);
}

NTSTATUS
OpenPolicy(
    LPWSTR ServerName,
    DWORD DesiredAccess,
    PLSA_HANDLE PolicyHandle
    )
{
    PLSA_UNICODE_STRING Server;
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    LSA_UNICODE_STRING ServerString;

    //
    // Always initialize the object attributes to all zeroes
    //
    ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

    if(ServerName != NULL) {
        //
        // Make a LSA_UNICODE_STRING out of the LPWSTR passed in
        //
        InitLsaString(&ServerString, ServerName);
        Server = &ServerString;
    } else {
        Server = NULL; // default to local machine
    }

    //
    // Attempt to open the policy and return NTSTATUS
    //
    return LsaOpenPolicy(
                Server,
                &ObjectAttributes,
                DesiredAccess,
                PolicyHandle
                );
}

void
DisplayNtStatus(
    LPSTR szAPI,
    NTSTATUS Status
    )
{
    //
    // convert the NTSTATUS to Winerror and DisplayWinError()
    //
    DisplayWinError(szAPI, LsaNtStatusToWinError(Status) );
}

void
DisplayWinError(
    LPSTR szAPI,    // pointer to Ansi function name
    DWORD dwError   // DWORD WinError
    )
{
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    //
    // TODO get this fprintf out of here!
    //
    fprintf(stderr,"%s error!\n", szAPI);

    if(dwBufferLength=FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM,
            NULL,
            dwError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPSTR) &MessageBuffer,
            0,
            NULL
            ))
    {
        DWORD dwBytesWritten; // unused

        //
        // Output message string on stderr
        //
        WriteFile(
                GetStdHandle(STD_ERROR_HANDLE),
                MessageBuffer,
                dwBufferLength,
                &dwBytesWritten,
                NULL
                );

        //
        // free the buffer allocated by the system
        //
        LocalFree(MessageBuffer);
    }
}


README.TXT

Check Security Descriptors (SD)

In the Win32 Programmer's Reference, if you go to the Security overview, then
choose the sub-topic "Allowing Access", you'll find the comment

   Note: It is fine to write code like this that builds security
   descriptors from scratch. It is, however, a good practice
   for people who write code that builds or manipulates
   security descriptors to first write code that explores the
   default security descriptors that Windows NT places on
   objects. For example, if Windows NT by default includes in
   a DACL an ACE granting the Local Logon SID certain access,
   it's good to know that, so that a decision not to grant any
   access to the Local Logon SID would be a conscious decision

This comment is accurate, however, to begin with, this task of examining the
SD is easier if there is sample code to start from. The purpose of the CHECK_SD
sample is to provide sample code you can start from as you examine an SD.
This sample only examines the SD on files, but the code can be modified to
examine the SD on other objects.

This sample is not a supported utility

To run
------

Type Check_sd to check the SD on the \\.\A: device

   Type Check_sd d:\a.fil to check the SD on the d:\a.fil
      file.  In this case d: must be formatted NTFS,
      because only NTFS files have SD(s)

Further notes
-------------

If you recompile with this define statement included:

  #define I_DO_NOT_WANT_THIS_CODE_TO_CLUTTER_THIS_PROGRAM_S_OUTPUT (1==0)

and re-run the program, the program will display more output, including
all the information you can access from the process Access Token, and the
SD's of some sample objects.

If you logon, run with the program built to produce the extra output as
mentioned, save that output to a file, log off and re-run the program,
and save the output of this second run to a different file, then you use
WinDiff to observe how the local logon SID changes values for each session.

Here is a sample test you could run to exercise DACLs involves using the
SD_FLPPY sample in conjunction with the CHECK_SD sample:

 - Log on to a machine as a local Administrator
 - Do
      check_sd \\.\A: >out_bef.a
      check_sd \\.\B: >out_bef.b
 - Logoff
 - Log on the same machine as Guest on the local machine domain
 - Do
      sd_flppy
 - Try
      dir a:    (observe access denied)
      dir b:    (observe access denied)
      copy config.sys a:\   (get device not found)
      copy config.sys b:\   (get device not found)
 - Logoff
 - Log on the same machine as a local Administrator
 - Do
      check_sd \\.\A: >out_aft.a
      check_sd \\.\B: >out_aft.b
 - Browse the differences between out_bef.* and out_aft.*

This test demonstrates that the ACLs that SD_FLPPY applies survive logoff.
To demonstrate that the DACLs do not survive logoff, simply reboot, log back
on as a local Administrator, and run the commands:

   check_sd \\.\A: >out_rbt.a
   check_sd \\.\B: >out_rbt.b

to see the DACLs are again as they were.


CHECK_SD.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/****************************************************************************\
*
* MODULE:       check_sd.c
*
*               In the Win32 .hlp file, if you click on Search, goto "Security
*                 Overview", then choose from the list of topics under
*                 Security Overview the sub-topic "Allowing Access", you'll
*                 find the comment
*
*                 Note:  It is fine to write code like this that builds
*                   security descriptors from scratch.  It is, however, a good
*                   practice for people who write code that builds or
*                   manipulates security descriptors to first write code that
*                   explores the default security descriptors that Windows NT
*                   places on objects.  For example, if Windows NT by default
*                   includes in a DACL an ACE granting the Local Logon SID
*                   certain access, it's good to know that, so that a decision
*                   not to grant any access to the Local Logon SID would be a
*                   conscious decision
*
*                 Note:  Obtaining the SACL from an object requires that the
*                   caller enables the SeSecurityPrivilege, and the object is
*                   opened with an additional access mask,
*                   ACCESS_SYSTEM_SECURITY.
*
*
* PURPOSE:      The comment in the .hlp file is accurate, however, for many
*                 people this task of examining the SD is easier if there is
*                 sample code to start from.  So, the purpose of this sample
*                 is to assist people by providing sample code people can
*                 start from as they examine SD(s).  This sample as is
*                 examines the SD on files, and this code can be modified to
*                 examine the SD on other objects
*
*               This sample is not a supported utility
*
* TO RUN:       Type Check_sd to check the SD on the \\.\A:  device
*
*               Type Check_sd d:\a.fil to check the SD on the d:\a.fil file.
*                 In this case d:  must be formatted NTFS, because only NTFS
*                 files have SD(s)
*
\****************************************************************************/

/****************************************************************************\
*  INCLUDES, DEFINES
\****************************************************************************/
#define STRICT
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>

#define PERR(api) printf("\n%s: Error %d from %s on line %d",  \
    __FILE__, GetLastError(), api, __LINE__);
#define PMSG(msg) printf("\n%s line %d: %s",  \
    __FILE__, __LINE__, msg);

/****************************************************************************\
* GLOBAL VARIABLES AND TYPEDEFS
\****************************************************************************/

typedef enum _KINDS_OF_ACCESSMASKS_DECODED {
    FileAccessMask,
    ProcessAccessMask,
    WindowStationAccessMask,
    DesktopAccessMask,
    RegKeyAccessMask,
    ServiceAccessMask,
    DefaultDaclInAccessTokenAccessMask
    } KINDS_OF_ACCESSMASKS_DECODED, * PKINDS_OF_ACCESSMASKS_DECODED;

// These hold the well-known SIDs

PSID psidNullSid;
PSID psidWorldSid;
PSID psidLocalSid;
PSID psidCreatorOwnerSid;
PSID psidCreatorGroupSid;
PSID psidNtAuthoritySid;
PSID psidDialupSid;
PSID psidNetworkSid;
PSID psidBatchSid;
PSID psidInteractiveSid;
PSID psidLogonIdsSid; // But the X and Y values are bogus at first!!! (See below)
PSID psidServiceSid;
PSID psidLocalSystemSid;
PSID psidBuiltinDomainSid;

/****************************************************************************\
* FUNCTION PROTOTYPES
\****************************************************************************/

VOID ExamineAccessToken(HANDLE hAccessToken);
BOOL ExamineSD    (PSECURITY_DESCRIPTOR psdSD,
                   KINDS_OF_ACCESSMASKS_DECODED kamKindOfMask);
BOOL ExamineACL   (PACL paclACL,              LPTSTR lpszOldIndent,
                   KINDS_OF_ACCESSMASKS_DECODED kamKindOfMask);
VOID ExamineMask  (ACCESS_MASK amMask,        LPTSTR lpszOldIndent,
                   KINDS_OF_ACCESSMASKS_DECODED kamKindOfMask);
BOOL LookupSIDName(PSID psidSID,              LPTSTR lpszOldIndent);
BOOL SIDStringName(PSID psidSID, LPTSTR lpszSIDStringName);
BOOL SetPrivilegeInAccessToken(VOID);
VOID InitializeWellKnownSIDs(VOID);
VOID DisplayHelp(VOID);

UINT main(UINT argc, char *argv[])
{

  HANDLE               hProcess;
  HANDLE               hAccessToken;
  #define         DEFAULT_FILE_TO_CHECK "\\\\.\\A:"
  #define                   SZ_NAME_BUF MAX_PATH
  UCHAR           ucPathBuf[SZ_NAME_BUF];
  LPTSTR          lpszFullName = (LPTSTR)&ucPathBuf;
  #define                           SZ_SD_BUF 8096
  UCHAR                ucBuf       [SZ_SD_BUF] = "";
  DWORD                dwSDLength = SZ_SD_BUF;
  DWORD                dwSDLengthNeeded;
  PSECURITY_DESCRIPTOR psdSD      = (PSECURITY_DESCRIPTOR)&ucBuf;
  DWORD                dwErrorMode;

 // check if running on Windows NT, if not, display notice and terminate

    if( GetVersion() & 0x80000000 )
    {
      MessageBox( NULL,
         "This sample application can only be run on Windows NT.\n"
         "This application will now terminate.",
         "CheckSD",
         MB_OK | MB_ICONSTOP | MB_SETFOREGROUND );
      return( 1 );
    }

  /**************************************************************************\
  *
  * This sample is not inside a Win32 service, however if this code were to be
  *   moved inside a Win32 service, the following defines and code that
  *   redirects stdout will be handy, because services cannot write to the
  *   screen
  *
  * You may wish to choose a different file name for the output file if you
  *   use this mechanism - note that the "w+" will destroy an existing file!
  *
  \**************************************************************************/

  #define WE_ARE_IN_A_SERVICE_SO_REDIRECT_STDOUT (0==1)
  #define   FILE_TO_REDIRECT_STDOUT_TO "c:\\check_sd.out"

  if     (WE_ARE_IN_A_SERVICE_SO_REDIRECT_STDOUT)
  { freopen(FILE_TO_REDIRECT_STDOUT_TO,"w+",stdout);
  }

  if      (1 == argc)
  { strcpy(lpszFullName,DEFAULT_FILE_TO_CHECK);
  }
  else if (2 == argc)
  { strcpy(lpszFullName,argv[1]);
  }
  else
  { DisplayHelp();
    return(0);
  }

  /**************************************************************************\
  *
  * Set up the well-known SID(s) in global variables, and enable the privilege
  *   needed in the access token to work with SACL(s)
  *
  \**************************************************************************/

  InitializeWellKnownSIDs();

  if (!SetPrivilegeInAccessToken())
  { return(1);
  }

  /**************************************************************************\
  *
  * This sample's primary purpose is to explore Security Descriptors.
  *   However, it is all too easy to over-focus on SD(s), while losing insight
  *   of the importance of Access Tokens.  So, we will now digress briefly to
  *   examine the access token of the current process
  *
  \**************************************************************************/

  hProcess = GetCurrentProcess();
  if (!hProcess)
  { PERR("GetCurrentProcess");
    return(1);
  }

  if (!OpenProcessToken(hProcess,
                        (TOKEN_READ | TOKEN_QUERY_SOURCE),
                        &hAccessToken))
  { PERR("OpenProcessToken");
    return(1);
  }

  ExamineAccessToken(hAccessToken);

  /**************************************************************************\
  *
  * Back to examining SD(s)
  *
  \**************************************************************************/

  printf("\nChecking SD on %s",lpszFullName);

  /**************************************************************************\
  *
  * SetErrorMode so we don't get the error due to no floppy disk in the floppy
  *   drive
  *
  \**************************************************************************/

  dwErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);

  if (!GetFileSecurity
        (lpszFullName,
         (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION
                               | GROUP_SECURITY_INFORMATION
                               | DACL_SECURITY_INFORMATION
                               | SACL_SECURITY_INFORMATION),
         psdSD,
         dwSDLength,
         (LPDWORD)&dwSDLengthNeeded))
  { PERR("GetFileSecurity");
    return(1);
  }

  SetErrorMode(dwErrorMode);

  if(!ExamineSD(psdSD,FileAccessMask))
  { PERR("ExamineSD failed");
    return(1);
  }

  /**************************************************************************\
  *
  * The above code showed how to examine an SD on a file.  There are SDs on
  *   other objects that could be examined by the function ExamineSD (and the
  *   other functions it calls).  The following are one example call each of
  *   the other four api's that are used to retrieve the SD from each of the
  *   types of Win32 objects that can have an SD
  *
  * These calls will execute properly without any work on your part, however,
  *   some work on your part will be required to get the sample calls below to
  *   show the SD for the objects your program uses!  You would need to make
  *   copy of this sample in a new directory, and write the code to get a
  *   handle to the object you're interested in, so you can pass that handle
  *   to the applicable api call below
  *
  * To insert the SD checking code into your own code you would do
  *   something like
  *
  * 1) Add the global variables above in your globals
  *
  * 2) Add the function prototypes above in your globals
  *
  * 3) Add the includes and PERR/PMSG macroes above to your code
  *
  * 4) Add all the functions defined below (except DisplayHelp and except
  *   ExamineAccessToken) to your code
  *
  * 5) Add a call sequence such as that in main() above that gets an
  *   SD and passes it to Examine SD.  Or add a call sequence such as
  *   one of those that follows that gets a handle, gets the SD on the
  *   object the handle addresses, then calls ExamineSD
  *
  * 6) If you are examining a type SD not already listed in
  *   KINDS_OF_ACCESSMASKS_DECODED, then you will have to add the new type to
  *   KINDS_OF_ACCESSMASKS_DECODED, and add to ExamineMask() the necessary
  *   code to crack that type of SD's access mask bits into the defines
  *
  \**************************************************************************/

  #define I_DO_NOT_WANT_THIS_CODE_TO_CLUTTER_THIS_PROGRAM_S_OUTPUT (0==0)

  if    (!I_DO_NOT_WANT_THIS_CODE_TO_CLUTTER_THIS_PROGRAM_S_OUTPUT)
  { HANDLE    hProcess;
    HANDLE    hWindowStation;
    HDESK     hDesktop;
    HKEY      hKey;
    SC_HANDLE schService;
    SC_HANDLE schSCManager;
    SECURITY_INFORMATION siSInfo =
         (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION
                               | GROUP_SECURITY_INFORMATION
                               | DACL_SECURITY_INFORMATION);

    printf("\n\nChecking SD on current process");

    hProcess = GetCurrentProcess();
    if (!hProcess)
    { PERR("GetCurrentProcess");
      return(1);
    }

    dwSDLength = SZ_SD_BUF;

    if (!GetKernelObjectSecurity
          (hProcess,
           (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION
                                 | GROUP_SECURITY_INFORMATION
                                 | DACL_SECURITY_INFORMATION),
           psdSD,
           dwSDLength,
           (LPDWORD)&dwSDLengthNeeded))
    { PERR("GetKernelObjectSecurity");
      return(1);
    }

    /************************************************************************\
    *
    * It is important to close all handles as soon as your code no longer
    *   needs them.  This conserves system resources.  In a sample such as
    *   this one, the practical effect is close to nil, since as soon as the
    *   sample exits (which only takes a few seconds, Windows NT destroys the
    *   process this sample was running in, which reclaims all resources
    *
    * However, in a program that creates many objects (such as threads), or
    *   where that program will be running for a long time, closing handles as
    *   soon as the program no longer needs them can save significant
    *   resources
    *
    * It is a good coding practice to make a habit of closing handles as soon
    *   as your code no longer needs the handle
    *
    \************************************************************************/

    CloseHandle(hProcess);

    if(!ExamineSD(psdSD,ProcessAccessMask))
    { PERR("ExamineSD failed");
      return(1);
    }

    printf("\n\nChecking SD on current Windowstation");

    hWindowStation = GetProcessWindowStation();
    if (INVALID_HANDLE_VALUE == hWindowStation)
    { PERR("GetProcessWindowStation");
      return(1);
    }

    dwSDLength = SZ_SD_BUF;

    if (!GetUserObjectSecurity
          (hWindowStation,
           &siSInfo,
           psdSD,
           dwSDLength,
           (LPDWORD)&dwSDLengthNeeded))
    { PERR("GetUserObjectSecurity");
      return(1);
    }

    CloseHandle(hWindowStation);

    if(!ExamineSD(psdSD,WindowStationAccessMask))
    { PERR("ExamineSD failed");
      return(1);
    }

    printf("\n\nChecking SD on current Desktop");

    hDesktop = GetThreadDesktop(GetCurrentThreadId());
    if (INVALID_HANDLE_VALUE == hDesktop)
    { PERR("GetThreadDesktop");
      return(1);
    }

    dwSDLength = SZ_SD_BUF;

    if (!GetUserObjectSecurity
          (hDesktop,
           &siSInfo,
           psdSD,
           dwSDLength,
           (LPDWORD)&dwSDLengthNeeded))
    { PERR("GetUserObjectSecurity");
      return(1);
    }

    CloseHandle(hDesktop);

    if(!ExamineSD(psdSD,DesktopAccessMask))
    { PERR("ExamineSD failed");
      return(1);
    }

    printf("\n\nChecking SD on registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet");

    if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                                      "SYSTEM\\CurrentControlSet",
                                      0,
                                      KEY_READ,
                                      &hKey))
    { PERR("RegOpenKeyEx");
      return(1);
    }

    dwSDLength = SZ_SD_BUF;

    if (ERROR_SUCCESS != RegGetKeySecurity
          (hKey,
           (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION
                                 | GROUP_SECURITY_INFORMATION
                                 | DACL_SECURITY_INFORMATION),
           psdSD,
           &dwSDLength))
    { PERR("RegGetKeySecurity");
      return(1);
    }

    RegCloseKey(hKey);

    if(!ExamineSD(psdSD,RegKeyAccessMask))
    { PERR("ExamineSD failed");
      return(1);
    }

    /************************************************************************\
    *
    * Any service will do here, just be sure to pick one that is in the
    *   Service Control Manager's database, and use the service name, not the
    *   service's display name.  For example, ClipSrv is displayed as Clipbook
    *   in the Services Control Panel applet
    *
    \************************************************************************/

    printf("\n\nChecking SD on service Clipbook");

    schSCManager = OpenSCManager(
                        NULL,                 // machine (NULL == local)
                        NULL,                 // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS // access required
                        );

    if (!schSCManager)
    { PERR("OpenSCManager");
      return(1);
    }

    schService = OpenService(schSCManager,"ClipSrv",SERVICE_ALL_ACCESS);
    if (!schService)
    { PERR("OpenService");
      return(1);
    }

    CloseServiceHandle(schSCManager);

    dwSDLength = SZ_SD_BUF;

    if (!QueryServiceObjectSecurity
          (schService,
           (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION
                                 | GROUP_SECURITY_INFORMATION
                                 | DACL_SECURITY_INFORMATION),
           psdSD,
           dwSDLength,
           (LPDWORD)&dwSDLengthNeeded))
    { PERR("QueryServiceObjectSecurity");
      return(1);
    }

    CloseServiceHandle(schService);

    if(!ExamineSD(psdSD,ServiceAccessMask))
    { PERR("ExamineSD failed");
      return(1);
    }
  }

  return(0);
}

/****************************************************************************\
*
* FUNCTION: ExamineSD
*
\****************************************************************************/

BOOL ExamineSD    (PSECURITY_DESCRIPTOR psdSD,
                   KINDS_OF_ACCESSMASKS_DECODED kamKindOfMask)
{

  PACL                        paclDACL;
  PACL                        paclSACL;
  BOOL                        bHasDACL        = FALSE;
  BOOL                        bHasSACL        = FALSE;
  BOOL                        bDaclDefaulted  = FALSE;
  BOOL                        bSaclDefaulted  = FALSE;
  BOOL                        bOwnerDefaulted = FALSE;
  BOOL                        bGroupDefaulted = FALSE;
  PSID                        psidOwner;
  PSID                        psidGroup;
  SECURITY_DESCRIPTOR_CONTROL sdcSDControl;
  DWORD                       dwSDRevision;
  DWORD                       dwSDLength;

  if (!IsValidSecurityDescriptor(psdSD))
  { PERR("IsValidSecurityDescriptor");
    return(FALSE);
  }

  dwSDLength = GetSecurityDescriptorLength(psdSD);

  if (!GetSecurityDescriptorDacl(psdSD,
                                 (LPBOOL)&bHasDACL,
                                 (PACL *)&paclDACL,
                                 (LPBOOL)&bDaclDefaulted))
  { PERR("GetSecurityDescriptorDacl");
    return(FALSE);
  }

  if (!GetSecurityDescriptorSacl(psdSD,
                                 (LPBOOL)&bHasSACL,
                                 (PACL *)&paclSACL,
                                 (LPBOOL)&bSaclDefaulted))
  { PERR("GetSecurityDescriptorSacl");
    return(FALSE);
  }

  if (!GetSecurityDescriptorOwner(psdSD,
                                  (PSID *)&psidOwner,
                                  (LPBOOL)&bOwnerDefaulted))
  { PERR("GetSecurityDescriptorOwner");
    return(FALSE);
  }

  if (!GetSecurityDescriptorGroup(psdSD,
                                  (PSID *)&psidGroup,
                                  (LPBOOL)&bGroupDefaulted))
  { PERR("GetSecurityDescriptorGroup");
    return(FALSE);
  }

  if (!GetSecurityDescriptorControl(psdSD,
                                    (PSECURITY_DESCRIPTOR_CONTROL)&sdcSDControl,
                                    (LPDWORD)&dwSDRevision))
  { PERR("GetSecurityDescriptorControl");
    return(FALSE);
  }

  printf("\nSD is valid.  SD is %d bytes long.  SD revision is %d == ",dwSDLength,dwSDRevision);

  switch (dwSDRevision)
  {
    case      SECURITY_DESCRIPTOR_REVISION1 :
    { printf("SECURITY_DESCRIPTOR_REVISION1");
      break;
    }
    default :
    { printf("! SD Revision is an IMPOSSIBLE SD revision!!! Perhaps a new revision was added...");
      return(FALSE);
    }
  }

  if (SE_SELF_RELATIVE & sdcSDControl)
    printf("\nSD is in self-relative format (all SDs returned by the system are)");

  if (NULL == psidOwner)
  { printf("\nSD's Owner is NULL, so SE_OWNER_DEFAULTED is ignored");
  }
  else
  { printf("\nSD's Owner is Not NULL");
    if (bOwnerDefaulted )
    { printf("\nSD's Owner-Defaulted flag is TRUE");
    }
    else
    { printf("\nSD's Owner-Defaulted flag is FALSE");
    }
    if(!LookupSIDName(psidOwner,""))
    { PERR("LookupSIDName failed");
    }
  }

  /**************************************************************************\
  *
  * The other use for psidGroup is for Macintosh client support
  *
  \**************************************************************************/

  if (NULL == psidGroup)
  { printf("\nSD's Group is NULL, so SE_GROUP_DEFAULTED is ignored");
    printf("\nSD's Group being NULL is typical, GROUP in SD(s) is mainly for POSIX compliance");
  }
  else
  { if (bGroupDefaulted)
    { printf("\nSD's Group-Defaulted flag is TRUE");
    }
    else
    { printf("\nSD's Group-Defaulted flag is FALSE");
    }
    if(!LookupSIDName(psidGroup,""))
    { PERR("LookupSIDName failed");
    }
  }

  if   (SE_DACL_PRESENT & sdcSDControl)
  { printf("\nSD's DACL is Present");
    if (bDaclDefaulted)
    { printf("\nSD's DACL-Defaulted flag is TRUE");
    }
    else
    { printf("\nSD's DACL-Defaulted flag is FALSE");
    }

    if (NULL == paclDACL)
    { printf("\nSD has a NULL DACL explicitly specified      (allows all access to Everyone)");
      printf("\n    This does not apply to this SD, but for comparison,");
      printf("\n    a non-NULL DACL pointer to a 0-length ACL allows  no access to   anyone");
    }
    else if(!ExamineACL(paclDACL,"",kamKindOfMask))
    {  PERR("ExamineACL failed");
    }
  }
  else
  { printf("\nSD's DACL is Not Present, so SE_DACL_DEFAULTED is ignored");
    printf("\nSD has no DACL at all (allows all access to Everyone)");
  }

  if   (SE_SACL_PRESENT & sdcSDControl)
  { printf("\nSD's SACL is Present");
    if (bSaclDefaulted)
    { printf("\nSD's SACL-Defaulted flag is TRUE");
    }
    else
    { printf("\nSD's SACL-Defaulted flag is FALSE");
    }

    if (NULL == paclSACL)
    { printf("\nSD has a NULL SACL explicitly specified");
    }
    else if(!ExamineACL(paclSACL,"",kamKindOfMask))
    {  PERR("ExamineACL failed");
    }
  }
  else
  { printf("\nSD's SACL is Not Present, so SE_SACL_DEFAULTED is ignored");
    printf("\nSD has no SACL at all (or we did not request to see it)");
  }
}

/****************************************************************************\
*
* FUNCTION: ExamineACL
*
\****************************************************************************/

BOOL ExamineACL   (PACL paclACL,              LPTSTR lpszOldIndent,
                   KINDS_OF_ACCESSMASKS_DECODED kamKindOfMask)
{
  #define                          SZ_INDENT_BUF 80
  UCHAR                ucIndentBuf[SZ_INDENT_BUF] = "";
  ACL_SIZE_INFORMATION                      asiAclSize;
  ACL_REVISION_INFORMATION                  ariAclRevision;
  DWORD                dwBufLength;
  DWORD                dwAcl_i;
  ACCESS_ALLOWED_ACE   *paaAllowedAce;

  strcpy(ucIndentBuf,lpszOldIndent);
  strcat(ucIndentBuf,"  ");

  if (!IsValidAcl(paclACL))
  { PERR("IsValidAcl");
    return(FALSE);
  }

  dwBufLength = sizeof(asiAclSize);

  if (!GetAclInformation(paclACL,
                         (LPVOID)&asiAclSize,
                         (DWORD)dwBufLength,
                         (ACL_INFORMATION_CLASS)AclSizeInformation))
  { PERR("GetAclInformation");
    return(FALSE);
  }

  dwBufLength = sizeof(ariAclRevision);

  if (!GetAclInformation(paclACL,
                         (LPVOID)&ariAclRevision,
                         (DWORD)dwBufLength,
                         (ACL_INFORMATION_CLASS)AclRevisionInformation))
  { PERR("GetAclInformation");
    return(FALSE);
  }

  printf("\n%sACL has %d ACE(s), %d bytes used, %d bytes free",ucIndentBuf,
     asiAclSize.AceCount,
     asiAclSize.AclBytesInUse,
     asiAclSize.AclBytesFree);

  printf("\n%sACL revision is %d == ",ucIndentBuf,ariAclRevision.AclRevision);

  switch (ariAclRevision.AclRevision)
  {
    case      ACL_REVISION1 :
    { printf("ACL_REVISION1");
      break;
    }
    case      ACL_REVISION2 :
    { printf("ACL_REVISION2");
      break;
    }
    default :
    { printf("\n%sACL Revision is an IMPOSSIBLE ACL revision!!! Perhaps a new revision was added...",ucIndentBuf);
      return(FALSE);
    }
  }

  for (dwAcl_i = 0; dwAcl_i < asiAclSize.AceCount;  dwAcl_i++)
  {
    if (!GetAce(paclACL,
                dwAcl_i,
                (LPVOID *)&paaAllowedAce))
    { PERR("GetAce");
      return(FALSE);
    }

    printf("\n%sACE %d size %d",ucIndentBuf,dwAcl_i,paaAllowedAce->Header.AceSize);

    { DWORD dwAceFlags = paaAllowedAce->Header.AceFlags;

      printf("\n%sACE %d flags 0x%.2x",ucIndentBuf,dwAcl_i,dwAceFlags);

      if (dwAceFlags)
      {
        DWORD dwExtraBits;
        UCHAR ucIndentBitsBuf[SZ_INDENT_BUF] = "";

        strcpy(ucIndentBitsBuf,ucIndentBuf);
        strcat(ucIndentBitsBuf,"            ");

        if ((dwAceFlags  & OBJECT_INHERIT_ACE        ) == OBJECT_INHERIT_ACE        )
        { printf("\n%s0x01 OBJECT_INHERIT_ACE        ",ucIndentBitsBuf);
        }
        if ((dwAceFlags  & CONTAINER_INHERIT_ACE     ) == CONTAINER_INHERIT_ACE     )
        { printf("\n%s0x02 CONTAINER_INHERIT_ACE     ",ucIndentBitsBuf);
        }
        if ((dwAceFlags  & NO_PROPAGATE_INHERIT_ACE  ) == NO_PROPAGATE_INHERIT_ACE  )
        { printf("\n%s0x04 NO_PROPAGATE_INHERIT_ACE  ",ucIndentBitsBuf);
        }
        if ((dwAceFlags  & INHERIT_ONLY_ACE          ) == INHERIT_ONLY_ACE          )
        { printf("\n%s0x08 INHERIT_ONLY_ACE          ",ucIndentBitsBuf);
        }
        if ((dwAceFlags  & VALID_INHERIT_FLAGS       ) == VALID_INHERIT_FLAGS       )
        { printf("\n%s0x0F VALID_INHERIT_FLAGS       ",ucIndentBitsBuf);
        }
        if ((dwAceFlags  & SUCCESSFUL_ACCESS_ACE_FLAG) == SUCCESSFUL_ACCESS_ACE_FLAG)
        { printf("\n%s0x40 SUCCESSFUL_ACCESS_ACE_FLAG",ucIndentBitsBuf);
        }
        if ((dwAceFlags  & FAILED_ACCESS_ACE_FLAG    ) == FAILED_ACCESS_ACE_FLAG    )
        { printf("\n%s0x80 FAILED_ACCESS_ACE_FLAG    ",ucIndentBitsBuf);
        }

        dwExtraBits = dwAceFlags & ( ~( OBJECT_INHERIT_ACE
                                      | CONTAINER_INHERIT_ACE
                                      | NO_PROPAGATE_INHERIT_ACE
                                      | INHERIT_ONLY_ACE
                                      | VALID_INHERIT_FLAGS
                                      | SUCCESSFUL_ACCESS_ACE_FLAG
                                      | FAILED_ACCESS_ACE_FLAG) );
        if (dwExtraBits)
        { printf("\n%sExtra AceFlag bits     == 0x%.8x <-This is a problem, should be all 0s",ucIndentBuf,dwExtraBits);
        }
      }
    }

    switch (paaAllowedAce->Header.AceType)
    {
      case                       ACCESS_ALLOWED_ACE_TYPE :
      { printf("\n%sACE %d is an ACCESS_ALLOWED_ACE_TYPE",ucIndentBuf,dwAcl_i);
        break;
      }
      case                       ACCESS_DENIED_ACE_TYPE :
      { printf("\n%sACE %d is an ACCESS_DENIED_ACE_TYPE",ucIndentBuf,dwAcl_i);
        break;
      }
      case                       SYSTEM_AUDIT_ACE_TYPE :
      { printf("\n%sACE %d is a  SYSTEM_AUDIT_ACE_TYPE",ucIndentBuf,dwAcl_i);
        break;
      }
      case                       SYSTEM_ALARM_ACE_TYPE :
      { printf("\n%sACE %d is a  SYSTEM_ALARM_ACE_TYPE",ucIndentBuf,dwAcl_i);
        break;
      }
      default :
      { printf("\n%sACE %d is an IMPOSSIBLE ACE_TYPE!!! Run debugger, examine value!",ucIndentBuf,dwAcl_i);
        return(FALSE);
      }
    }

    printf("\n%sACE %d mask               == 0x%.8x",ucIndentBuf,dwAcl_i,paaAllowedAce->Mask);

    ExamineMask(paaAllowedAce->Mask,ucIndentBuf,kamKindOfMask);

    if(!LookupSIDName((PSID)&(paaAllowedAce->SidStart),ucIndentBuf))
    { PERR("LookupSIDName failed");
    }
  }
}

/****************************************************************************\
*
* FUNCTION: ExamineMask
*
\****************************************************************************/

VOID ExamineMask  (ACCESS_MASK amMask,        LPTSTR lpszOldIndent,
                   KINDS_OF_ACCESSMASKS_DECODED kamKindOfMask)
{
  #define STANDARD_RIGHTS_ALL_THE_BITS 0x00FF0000L
  #define GENERIC_RIGHTS_ALL_THE_BITS  0xF0000000L
  UCHAR ucIndentBuf[SZ_INDENT_BUF]     = "";
  UCHAR ucIndentBitsBuf[SZ_INDENT_BUF] = "";
  DWORD dwGenericBits;
  DWORD dwStandardBits;
  DWORD dwSpecificBits;
  DWORD dwAccessSystemSecurityBit;
  DWORD dwExtraBits;

  strcpy(ucIndentBuf,    lpszOldIndent);
  strcat(ucIndentBuf,    "  ");

strcpy(ucIndentBitsBuf,lpszOldIndent);
  strcat(ucIndentBitsBuf,"                            ");

  dwStandardBits            = (amMask & STANDARD_RIGHTS_ALL_THE_BITS);
  dwSpecificBits            = (amMask & SPECIFIC_RIGHTS_ALL         );
  dwAccessSystemSecurityBit = (amMask & ACCESS_SYSTEM_SECURITY      );
  dwGenericBits             = (amMask & GENERIC_RIGHTS_ALL_THE_BITS );

  /**************************************************************************\
  *
  * Print then decode the standard rights bits
  *
  \**************************************************************************/

  printf("\n%sStandard Rights        == 0x%.8x",ucIndentBuf,dwStandardBits);

  if (dwStandardBits)
  {
    if ((dwStandardBits    & DELETE                  ) == DELETE                  )
    { printf("\n%s0x00010000 DELETE                  ",ucIndentBitsBuf);
    }
    if ((dwStandardBits    & READ_CONTROL            ) == READ_CONTROL            )
    { printf("\n%s0x00020000 READ_CONTROL            ",ucIndentBitsBuf);
    }
    if ((dwStandardBits    & STANDARD_RIGHTS_READ    ) == STANDARD_RIGHTS_READ    )
    { printf("\n%s0x00020000 STANDARD_RIGHTS_READ    ",ucIndentBitsBuf);
    }
    if ((dwStandardBits    & STANDARD_RIGHTS_WRITE   ) == STANDARD_RIGHTS_WRITE   )
    { printf("\n%s0x00020000 STANDARD_RIGHTS_WRITE   ",ucIndentBitsBuf);
    }
    if ((dwStandardBits    & STANDARD_RIGHTS_EXECUTE ) == STANDARD_RIGHTS_EXECUTE )
    { printf("\n%s0x00020000 STANDARD_RIGHTS_EXECUTE ",ucIndentBitsBuf);
    }
    if ((dwStandardBits    & WRITE_DAC               ) == WRITE_DAC               )
    { printf("\n%s0x00040000 WRITE_DAC               ",ucIndentBitsBuf);
    }
    if ((dwStandardBits    & WRITE_OWNER             ) == WRITE_OWNER             )
    { printf("\n%s0x00080000 WRITE_OWNER             ",ucIndentBitsBuf);
    }
    if ((dwStandardBits    & SYNCHRONIZE             ) == SYNCHRONIZE             )
    { printf("\n%s0x00100000 SYNCHRONIZE             ",ucIndentBitsBuf);
    }
    if ((dwStandardBits    & STANDARD_RIGHTS_REQUIRED) == STANDARD_RIGHTS_REQUIRED)
    { printf("\n%s0x000F0000 STANDARD_RIGHTS_REQUIRED",ucIndentBitsBuf);
    }
    if ((dwStandardBits    & STANDARD_RIGHTS_ALL     ) == STANDARD_RIGHTS_ALL     )
    { printf("\n%s0x001F0000 STANDARD_RIGHTS_ALL     ",ucIndentBitsBuf);
    }

    dwExtraBits = dwStandardBits & ( ~( DELETE
                                      | READ_CONTROL
                                      | STANDARD_RIGHTS_READ
                                      | STANDARD_RIGHTS_WRITE
                                      | STANDARD_RIGHTS_EXECUTE
                                      | WRITE_DAC
                                      | WRITE_OWNER
                                      | SYNCHRONIZE
                                      | STANDARD_RIGHTS_REQUIRED
                                      | STANDARD_RIGHTS_ALL) );
    if (dwExtraBits)
    { printf("\n%sExtra standard bits    == 0x%.8x <-This is a problem, should be all 0s",ucIndentBuf,dwExtraBits);
    }
  }

  /**************************************************************************\
  *
  * Print then decode the specific rights bits
  *
  \**************************************************************************/

  printf("\n%sSpecific Rights        == 0x%.8x",ucIndentBuf,dwSpecificBits);

  if (dwSpecificBits)
  {
    if      (FileAccessMask          == kamKindOfMask)
    {
      if ((dwSpecificBits    & FILE_READ_DATA           ) == FILE_READ_DATA           )
      { printf("\n%s0x00000001 FILE_READ_DATA            (file & pipe)     ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_LIST_DIRECTORY      ) == FILE_LIST_DIRECTORY      )
      { printf("\n%s0x00000001 FILE_LIST_DIRECTORY       (directory)       ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_WRITE_DATA          ) == FILE_WRITE_DATA          )
      { printf("\n%s0x00000002 FILE_WRITE_DATA           (file & pipe)     ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_ADD_FILE            ) == FILE_ADD_FILE            )
      { printf("\n%s0x00000002 FILE_ADD_FILE             (directory)       ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_APPEND_DATA         ) == FILE_APPEND_DATA         )
      { printf("\n%s0x00000004 FILE_APPEND_DATA          (file)            ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_ADD_SUBDIRECTORY    ) == FILE_ADD_SUBDIRECTORY    )
      { printf("\n%s0x00000004 FILE_ADD_SUBDIRECTORY     (directory)       ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_CREATE_PIPE_INSTANCE) == FILE_CREATE_PIPE_INSTANCE)
      { printf("\n%s0x00000004 FILE_CREATE_PIPE_INSTANCE (named pipe)      ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_READ_EA             ) == FILE_READ_EA             )
      { printf("\n%s0x00000008 FILE_READ_EA              (file & directory)",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_WRITE_EA            ) == FILE_WRITE_EA            )
      { printf("\n%s0x00000010 FILE_WRITE_EA             (file & directory)",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_EXECUTE             ) == FILE_EXECUTE             )
      { printf("\n%s0x00000020 FILE_EXECUTE              (file)            ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_TRAVERSE            ) == FILE_TRAVERSE            )
      { printf("\n%s0x00000020 FILE_TRAVERSE             (directory)       ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_DELETE_CHILD        ) == FILE_DELETE_CHILD        )
      { printf("\n%s0x00000040 FILE_DELETE_CHILD         (directory)       ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_READ_ATTRIBUTES     ) == FILE_READ_ATTRIBUTES     )
      { printf("\n%s0x00000080 FILE_READ_ATTRIBUTES      (all)             ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & FILE_WRITE_ATTRIBUTES    ) == FILE_WRITE_ATTRIBUTES    )
      { printf("\n%s0x00000100 FILE_WRITE_ATTRIBUTES     (all)             ",ucIndentBitsBuf);
      }

      if (((dwStandardBits | dwSpecificBits   )
           & FILE_ALL_ACCESS     ) == FILE_ALL_ACCESS     )
      { printf("\n%s0x001F01FF FILE_ALL_ACCESS      == (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)",ucIndentBitsBuf);
      }
      if (((dwStandardBits | dwSpecificBits   )
           & FILE_GENERIC_READ   ) == FILE_GENERIC_READ   )
      { printf("\n%s0x00120089 FILE_GENERIC_READ    == (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE)",ucIndentBitsBuf);
      }
      if (((dwStandardBits | dwSpecificBits   )
           & FILE_GENERIC_WRITE  ) == FILE_GENERIC_WRITE  )
      { printf("\n%s0x00120116 FILE_GENERIC_WRITE   == (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE)",ucIndentBitsBuf);
      }
      if (((dwStandardBits | dwSpecificBits   )
           & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE)
      { printf("\n%s0x001200A0 FILE_GENERIC_EXECUTE == (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE)",ucIndentBitsBuf);
      }

      dwExtraBits = dwSpecificBits & ( ~( FILE_READ_DATA
                                        | FILE_LIST_DIRECTORY
                                        | FILE_WRITE_DATA
                                        | FILE_ADD_FILE
                                        | FILE_APPEND_DATA
                                        | FILE_ADD_SUBDIRECTORY
                                        | FILE_CREATE_PIPE_INSTANCE
                                        | FILE_READ_EA
                                        | FILE_WRITE_EA
                                        | FILE_EXECUTE
                                        | FILE_TRAVERSE
                                        | FILE_DELETE_CHILD
                                        | FILE_READ_ATTRIBUTES
                                        | FILE_WRITE_ATTRIBUTES
                                        | (FILE_ALL_ACCESS      & SPECIFIC_RIGHTS_ALL)
                                        | (FILE_GENERIC_READ    & SPECIFIC_RIGHTS_ALL)
                                        | (FILE_GENERIC_WRITE   & SPECIFIC_RIGHTS_ALL)
                                        | (FILE_GENERIC_EXECUTE & SPECIFIC_RIGHTS_ALL) ) );
      if (dwExtraBits)
      { printf("\n%sExtra specific bits    == 0x%.8x <-This is a problem, should be all 0s",ucIndentBuf,dwExtraBits);
      }
    }
    else if (ProcessAccessMask       == kamKindOfMask)
    {
      if ((dwSpecificBits    & PROCESS_TERMINATE        ) == PROCESS_TERMINATE        )
      { printf("\n%s0x00000001 PROCESS_TERMINATE        ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & PROCESS_CREATE_THREAD    ) == PROCESS_CREATE_THREAD    )
      { printf("\n%s0x00000002 PROCESS_CREATE_THREAD    ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & PROCESS_VM_OPERATION     ) == PROCESS_VM_OPERATION     )
      { printf("\n%s0x00000008 PROCESS_VM_OPERATION     ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & PROCESS_VM_READ          ) == PROCESS_VM_READ          )
      { printf("\n%s0x00000010 PROCESS_VM_READ          ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & PROCESS_VM_WRITE         ) == PROCESS_VM_WRITE         )
      { printf("\n%s0x00000020 PROCESS_VM_WRITE         ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & PROCESS_DUP_HANDLE       ) == PROCESS_DUP_HANDLE       )
      { printf("\n%s0x00000040 PROCESS_DUP_HANDLE       ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & PROCESS_CREATE_PROCESS   ) == PROCESS_CREATE_PROCESS   )
      { printf("\n%s0x00000080 PROCESS_CREATE_PROCESS   ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & PROCESS_SET_INFORMATION  ) == PROCESS_SET_INFORMATION  )
      { printf("\n%s0x00000200 PROCESS_SET_INFORMATION  ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & PROCESS_QUERY_INFORMATION) == PROCESS_QUERY_INFORMATION)
      { printf("\n%s0x00000400 PROCESS_QUERY_INFORMATION",ucIndentBitsBuf);
      }

      if (((dwStandardBits | dwSpecificBits )
           & PROCESS_ALL_ACCESS) == PROCESS_ALL_ACCESS)
      { printf("\n%s0x001F0FFF PROCESS_ALL_ACCESS == (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x00000FFF) ",ucIndentBitsBuf);
      }

      dwExtraBits = dwSpecificBits & ( ~( PROCESS_TERMINATE
                                        | PROCESS_CREATE_THREAD
                                        | PROCESS_VM_OPERATION
                                        | PROCESS_VM_READ
                                        | PROCESS_VM_WRITE
                                        | PROCESS_DUP_HANDLE
                                        | PROCESS_CREATE_PROCESS
                                        | PROCESS_SET_INFORMATION
                                        | PROCESS_QUERY_INFORMATION
                                        | (PROCESS_ALL_ACCESS & SPECIFIC_RIGHTS_ALL) ) );
      if (dwExtraBits)
      { printf("\n%sExtra specific bits    == 0x%.8x <-This is a problem, should be all 0s",ucIndentBuf,dwExtraBits);
      }
    }
    else if (WindowStationAccessMask == kamKindOfMask)
    {
      if ((dwSpecificBits    & WINSTA_ENUMDESKTOPS     ) == WINSTA_ENUMDESKTOPS     )
      { printf("\n%s0x00000001 WINSTA_ENUMDESKTOPS     ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & WINSTA_READATTRIBUTES   ) == WINSTA_READATTRIBUTES   )
      { printf("\n%s0x00000002 WINSTA_READATTRIBUTES   ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & WINSTA_ACCESSCLIPBOARD  ) == WINSTA_ACCESSCLIPBOARD  )
      { printf("\n%s0x00000004 WINSTA_ACCESSCLIPBOARD  ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & WINSTA_CREATEDESKTOP    ) == WINSTA_CREATEDESKTOP    )
      { printf("\n%s0x00000008 WINSTA_CREATEDESKTOP    ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & WINSTA_WRITEATTRIBUTES  ) == WINSTA_WRITEATTRIBUTES  )
      { printf("\n%s0x00000010 WINSTA_WRITEATTRIBUTES  ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & WINSTA_ACCESSGLOBALATOMS) == WINSTA_ACCESSGLOBALATOMS)
      { printf("\n%s0x00000020 WINSTA_ACCESSGLOBALATOMS",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & WINSTA_EXITWINDOWS      ) == WINSTA_EXITWINDOWS      )
      { printf("\n%s0x00000040 WINSTA_EXITWINDOWS      ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & WINSTA_ENUMERATE        ) == WINSTA_ENUMERATE        )
      { printf("\n%s0x00000100 WINSTA_ENUMERATE        ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & WINSTA_READSCREEN       ) == WINSTA_READSCREEN       )
      { printf("\n%s0x00000200 WINSTA_READSCREEN       ",ucIndentBitsBuf);
      }

      dwExtraBits = dwSpecificBits & ( ~( WINSTA_ENUMDESKTOPS
                                        | WINSTA_READATTRIBUTES
                                        | WINSTA_ACCESSCLIPBOARD
                                        | WINSTA_CREATEDESKTOP
                                        | WINSTA_WRITEATTRIBUTES
                                        | WINSTA_ACCESSGLOBALATOMS
                                        | WINSTA_EXITWINDOWS
                                        | WINSTA_ENUMERATE
                                        | WINSTA_READSCREEN) );
      if (dwExtraBits)
      { printf("\n%sExtra specific bits    == 0x%.8x <-This is a problem, should be all 0s",ucIndentBuf,dwExtraBits);
      }
    }
    else if (DesktopAccessMask == kamKindOfMask)
    {
      if ((dwSpecificBits    & DESKTOP_READOBJECTS     ) == DESKTOP_READOBJECTS     )
      { printf("\n%s0x00000001 DESKTOP_READOBJECTS     ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & DESKTOP_CREATEWINDOW    ) == DESKTOP_CREATEWINDOW    )
      { printf("\n%s0x00000002 DESKTOP_CREATEWINDOW    ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & DESKTOP_CREATEMENU      ) == DESKTOP_CREATEMENU      )
      { printf("\n%s0x00000004 DESKTOP_CREATEMENU      ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & DESKTOP_HOOKCONTROL     ) == DESKTOP_HOOKCONTROL     )
      { printf("\n%s0x00000008 DESKTOP_HOOKCONTROL     ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & DESKTOP_JOURNALRECORD   ) == DESKTOP_JOURNALRECORD   )
      { printf("\n%s0x00000010 DESKTOP_JOURNALRECORD   ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & DESKTOP_JOURNALPLAYBACK ) == DESKTOP_JOURNALPLAYBACK )
      { printf("\n%s0x00000020 DESKTOP_JOURNALPLAYBACK ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & DESKTOP_ENUMERATE       ) == DESKTOP_ENUMERATE       )
      { printf("\n%s0x00000040 DESKTOP_ENUMERATE       ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & DESKTOP_WRITEOBJECTS    ) == DESKTOP_WRITEOBJECTS    )
      { printf("\n%s0x00000080 DESKTOP_WRITEOBJECTS    ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & DESKTOP_SWITCHDESKTOP   ) == DESKTOP_SWITCHDESKTOP   )
      { printf("\n%s0x00000100 DESKTOP_SWITCHDESKTOP   ",ucIndentBitsBuf);
      }

      dwExtraBits = dwSpecificBits & ( ~( DESKTOP_READOBJECTS
                                        | DESKTOP_CREATEWINDOW
                                        | DESKTOP_CREATEMENU
                                        | DESKTOP_HOOKCONTROL
                                        | DESKTOP_JOURNALRECORD
                                        | DESKTOP_JOURNALPLAYBACK
                                        | DESKTOP_ENUMERATE
                                        | DESKTOP_WRITEOBJECTS
                                        | DESKTOP_SWITCHDESKTOP) );
      if (dwExtraBits)
      { printf("\n%sExtra specific bits    == 0x%.8x <-This is a problem, should be all 0s",ucIndentBuf,dwExtraBits);
      }
    }
    else if (RegKeyAccessMask        == kamKindOfMask)
    {
      if ((dwSpecificBits    & KEY_QUERY_VALUE       ) == KEY_QUERY_VALUE       )
      { printf("\n%s0x00000001 KEY_QUERY_VALUE       ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & KEY_SET_VALUE         ) == KEY_SET_VALUE         )
      { printf("\n%s0x00000002 KEY_SET_VALUE         ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & KEY_CREATE_SUB_KEY    ) == KEY_CREATE_SUB_KEY    )
      { printf("\n%s0x00000004 KEY_CREATE_SUB_KEY    ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & KEY_ENUMERATE_SUB_KEYS) == KEY_ENUMERATE_SUB_KEYS)
      { printf("\n%s0x00000008 KEY_ENUMERATE_SUB_KEYS",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & KEY_NOTIFY            ) == KEY_NOTIFY            )
      { printf("\n%s0x00000010 KEY_NOTIFY            ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & KEY_CREATE_LINK       ) == KEY_CREATE_LINK       )
      { printf("\n%s0x00000020 KEY_CREATE_LINK       ",ucIndentBitsBuf);
      }

      if (((dwStandardBits | dwSpecificBits   )
           & KEY_READ      ) == KEY_READ      )
      { printf("\n%s0x00020019 KEY_READ       == ((STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & (~SYNCHRONIZE))",ucIndentBitsBuf);
      }
      if (((dwStandardBits | dwSpecificBits   )
           & KEY_WRITE     ) == KEY_WRITE     )
      { printf("\n%s0x00020006 KEY_WRITE      == ((STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) & (~SYNCHRONIZE))",ucIndentBitsBuf);
      }
      if (((dwStandardBits | dwSpecificBits   )
           & KEY_EXECUTE   ) == KEY_EXECUTE   )
      { printf("\n%s0x00020019 KEY_EXECUTE    == ((KEY_READ) & (~SYNCHRONIZE))",ucIndentBitsBuf);
      }
      if (((dwStandardBits | dwSpecificBits   )
           & KEY_ALL_ACCESS) == KEY_ALL_ACCESS)
      { printf("\n%s0x000F003F KEY_ALL_ACCESS == ((STANDARD_RIGHTS_ALL | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK) & (~SYNCHRONIZE))",ucIndentBitsBuf);
      }

      dwExtraBits = dwSpecificBits & ( ~( KEY_QUERY_VALUE
                                        | KEY_SET_VALUE
                                        | KEY_CREATE_SUB_KEY
                                        | KEY_ENUMERATE_SUB_KEYS
                                        | KEY_NOTIFY
                                        | KEY_CREATE_LINK
                                        | (KEY_READ       & SPECIFIC_RIGHTS_ALL)
                                        | (KEY_WRITE      & SPECIFIC_RIGHTS_ALL)
                                        | (KEY_EXECUTE    & SPECIFIC_RIGHTS_ALL)
                                        | (KEY_ALL_ACCESS & SPECIFIC_RIGHTS_ALL) ) );
      if (dwExtraBits)
      { printf("\n%sExtra specific bits    == 0x%.8x <-This is a problem, should be all 0s",ucIndentBuf,dwExtraBits);
      }
    }
    else if (ServiceAccessMask       == kamKindOfMask)
    {
      if ((dwSpecificBits    & SERVICE_QUERY_CONFIG        ) == SERVICE_QUERY_CONFIG        )
      { printf("\n%s0x00000001 SERVICE_QUERY_CONFIG        ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & SERVICE_CHANGE_CONFIG       ) == SERVICE_CHANGE_CONFIG       )
      { printf("\n%s0x00000002 SERVICE_CHANGE_CONFIG       ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & SERVICE_QUERY_STATUS        ) == SERVICE_QUERY_STATUS        )
      { printf("\n%s0x00000004 SERVICE_QUERY_STATUS        ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & SERVICE_ENUMERATE_DEPENDENTS) == SERVICE_ENUMERATE_DEPENDENTS)
      { printf("\n%s0x00000008 SERVICE_ENUMERATE_DEPENDENTS",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & SERVICE_START               ) == SERVICE_START               )
      { printf("\n%s0x00000010 SERVICE_START               ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & SERVICE_STOP                ) == SERVICE_STOP                )
      { printf("\n%s0x00000020 SERVICE_STOP                ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & SERVICE_PAUSE_CONTINUE      ) == SERVICE_PAUSE_CONTINUE      )
      { printf("\n%s0x00000040 SERVICE_PAUSE_CONTINUE      ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & SERVICE_INTERROGATE         ) == SERVICE_INTERROGATE         )
      { printf("\n%s0x00000080 SERVICE_INTERROGATE         ",ucIndentBitsBuf);
      }
      if ((dwSpecificBits    & SERVICE_USER_DEFINED_CONTROL) == SERVICE_USER_DEFINED_CONTROL)
      { printf("\n%s0x00000100 SERVICE_USER_DEFINED_CONTROL",ucIndentBitsBuf,ucIndentBitsBuf);
      }

      if (((dwStandardBits | dwSpecificBits )
           & SERVICE_ALL_ACCESS) == SERVICE_ALL_ACCESS)
      { printf("\n%s0x000F01FF SERVICE_ALL_ACCESS == (STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | ", ucIndentBitsBuf );
        printf("%s", "SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL)" );
      }

      dwExtraBits = dwSpecificBits & ( ~( SERVICE_QUERY_CONFIG
                                        | SERVICE_CHANGE_CONFIG
                                        | SERVICE_QUERY_STATUS
                                        | SERVICE_ENUMERATE_DEPENDENTS
                                        | SERVICE_START
                                        | SERVICE_STOP
                                        | SERVICE_PAUSE_CONTINUE
                                        | SERVICE_INTERROGATE
                                        | SERVICE_USER_DEFINED_CONTROL
                                        | (SERVICE_ALL_ACCESS & SPECIFIC_RIGHTS_ALL) ) );
      if (dwExtraBits)
      { printf("\n%sExtra specific bits    == 0x%.8x <-This is a problem, should be all 0s",ucIndentBuf,dwExtraBits);
      }
    }
    else if (DefaultDaclInAccessTokenAccessMask == kamKindOfMask)
    {
      printf("\n%sSpecific bits in default Dacl(s) in token not broken down into defines",ucIndentBitsBuf);
    }
    else
    { printf("\n%sYou will need to write some code (such as that directly",ucIndentBuf);
      printf("\n%s  above the code that wrote out this message) to decode",ucIndentBuf);
      printf("\n%s  this kind of access mask",ucIndentBuf);
    }
  }

  /**************************************************************************\
  *
  * Print then decode the ACCESS_SYSTEM_SECURITY bit
  *
  \**************************************************************************/

  printf("\n%sAccess System Security == 0x%.8x",ucIndentBuf,dwAccessSystemSecurityBit);

  /**************************************************************************\
  *
  * Print then decode the generic rights bits, which will rarely be on
  *
  * Generic bits are nearly always mapped by Windows NT before it tries to do
  *   anything with them.  You can ignore the fact that generic bits are
  *   special in any way, although it helps to keep track of what the mappings
  *   are so that you don't have any surprises
  *
  * The only time the generic bits are not mapped immediately is if they are
  *   placed in an inheritable ACE in an ACL, or in an ACL that will be
  *   assigned by default (such as the default DACL in an access token).  In
  *   that case they're mapped when the child object is created (or when the
  *   default DACL is used at object creation time)
  *
  \**************************************************************************/

  printf("\n%sGeneric  Rights        == 0x%.8x",ucIndentBuf,dwGenericBits);

  if (dwGenericBits)
  {
    if ((dwGenericBits     & GENERIC_READ   ) == GENERIC_READ   )
    { printf("\n%s0x80000000 GENERIC_READ   ",ucIndentBitsBuf);
    }
    if ((dwGenericBits     & GENERIC_WRITE  ) == GENERIC_WRITE  )
    { printf("\n%s0x40000000 GENERIC_WRITE  ",ucIndentBitsBuf);
    }
    if ((dwGenericBits     & GENERIC_EXECUTE) == GENERIC_EXECUTE)
    { printf("\n%s0x20000000 GENERIC_EXECUTE",ucIndentBitsBuf);
    }
    if ((dwGenericBits     & GENERIC_ALL    ) == GENERIC_ALL    )
    { printf("\n%s0x10000000 GENERIC_ALL    ",ucIndentBitsBuf);
    }

    dwExtraBits = dwGenericBits & ( ~( GENERIC_READ
                                     | GENERIC_WRITE
                                     | GENERIC_EXECUTE
                                     | GENERIC_ALL) );
    if (dwExtraBits)
    { printf("\n%sExtra generic bits     == 0x%.8x <-This is a problem, should be all 0s",ucIndentBuf,dwExtraBits);
    }
  }
}

/****************************************************************************\
*
* FUNCTION: LookupSIDName
*
\****************************************************************************/

BOOL LookupSIDName(PSID psidSID, LPTSTR lpszOldIndent)
{
  UCHAR        ucIndentBuf    [SZ_INDENT_BUF]    = "";
  #define                      SZ_ACCT_NAME_BUF  60
  UCHAR        ucNameBuf      [SZ_ACCT_NAME_BUF] = "";
  DWORD        dwNameLength  = SZ_ACCT_NAME_BUF;
  #define                      SZ_DMN_NAME_BUF   60
  UCHAR        ucDomainNmBuf  [SZ_DMN_NAME_BUF]  = "";
  DWORD        dwDNameLength = SZ_DMN_NAME_BUF;
  #define                      SZ_SID_STRING_BUF 150
  UCHAR        ucSIDStringBuf [SZ_SID_STRING_BUF] = "";
  SID_NAME_USE peAcctNameUse = SidTypeInvalid;
  DWORD        dwLookupStatus;
  BOOL         bGotBadLookupThatIsNotLocalLogonSID;

  strcpy(ucIndentBuf,lpszOldIndent);
  strcat(ucIndentBuf,"  ");

  if (!IsValidSid(psidSID))
  { PERR("IsValidSid");
    return(FALSE);
  }

  if (!SIDStringName(psidSID,ucSIDStringBuf))
  { PERR("SIDStringName");
    return(FALSE);
  }

  if (!LookupAccountSid(
         (LPTSTR)"",         // Look on local machine
         psidSID,
         (LPTSTR)&ucNameBuf,
         (LPDWORD)&dwNameLength,
         (LPTSTR)&ucDomainNmBuf,
         (LPDWORD)&dwDNameLength,
         (PSID_NAME_USE)&peAcctNameUse))
  {
    dwLookupStatus = GetLastError();

    /************************************************************************\
    *
    * Got a bad Lookup, so check is SID the Local Logon SID?
    *
    * The problem is that LookupAccountSid api will find all the well-known
    *   SIDs except the Local Logon SID.  The last two sub-authorities are
    *   always different, so to check to see if the SID we're looking at is
    *   the Local Logon SID, we take the psidLogonIdsSid variable we built at
    *   initialization time, and blast into it's last two sub-authorities the
    *   last two sub-authorities that we have.  Then compare for EqualSid
    *
    \************************************************************************/

    // Must have same number of sub authorities

    bGotBadLookupThatIsNotLocalLogonSID = FALSE;  // Assume the best :)

    if ( ( *(GetSidSubAuthorityCount(psidLogonIdsSid))) !=
         ( *(GetSidSubAuthorityCount(psidSID        )))    )
    { // Not same number of sub-authorities, so can't be a match
      bGotBadLookupThatIsNotLocalLogonSID = TRUE;
    }
    else
    {
      // Force the last two sub-authorities to match
      *(GetSidSubAuthority( psidLogonIdsSid, 1 )) =
      *(GetSidSubAuthority( psidSID        , 1 ));
      *(GetSidSubAuthority( psidLogonIdsSid, 2 )) =
      *(GetSidSubAuthority( psidSID        , 2 ));

      /**********************************************************************\
      *
      * EqualPrefixSid could be used instead if we want to blast in all but
      *   the last sub-authority.  For demonstration purposes, as long as we
      *   did one of the two previous assignment statements, we may as well to
      *   the other and use EqualSID
      *
      \**********************************************************************/

      if (EqualSid(psidSID,psidLogonIdsSid))
      { printf("\n%sSID is the Local Logon SID   %s",ucIndentBuf,ucSIDStringBuf);
      }
      else
      { bGotBadLookupThatIsNotLocalLogonSID = TRUE;
      }
    }
    if (bGotBadLookupThatIsNotLocalLogonSID)
    {
      /**********************************************************************\
      *
      * ERROR_NONE_MAPPED means account unknown.  RegEdt32.exe will show
      *   1332-error-type accounts as Account Unknown, so we will also
      *
      \**********************************************************************/

      if (ERROR_NONE_MAPPED == dwLookupStatus)
      { printf("\n%sSID domain == %s, Name == %s    (Account Unknown)  %s",ucIndentBuf,ucDomainNmBuf,ucNameBuf,ucSIDStringBuf);
      }
      else
      { SetLastError(dwLookupStatus);
        PERR("LookupAccountSid");
        return(FALSE);
      }
    }
  }
  else
  { // Got good Lookup, so SID Is NOT the Local Logon SID
    printf("\n%sSID domain == %s, Name == %s   %s",ucIndentBuf,ucDomainNmBuf,ucNameBuf,ucSIDStringBuf);

    /************************************************************************\
    *
    * For demonstration purposes see which well-known SID it might be
    * For demonstration purposes do a silly search demonstrating
    *   no two well-known SIDs are equal
    *
    \************************************************************************/

    if (EqualSid(psidSID,psidNullSid))
    { printf("\n%sSID is the Null SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidWorldSid))
    { printf("\n%sSID is the World SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidLocalSid))
    { printf("\n%sSID is the Local SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidCreatorOwnerSid))
    { printf("\n%sSID is the CreatorOwner SID",ucIndentBuf);

}
    if (EqualSid(psidSID,psidCreatorGroupSid))
    { printf("\n%sSID is the CreatorGroup SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidNtAuthoritySid))
    { printf("\n%sSID is the NtAuthority SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidDialupSid))
    { printf("\n%sSID is the DialUp SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidNetworkSid))
    { printf("\n%sSID is the Network SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidBatchSid))
    { printf("\n%sSID is the Batch SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidInteractiveSid))
    { printf("\n%sSID is the Interactive SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidServiceSid))
    { printf("\n%sSID is the Service SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidLocalSystemSid))
    { printf("\n%sSID is the LocalSystem SID",ucIndentBuf);
    }
    if (EqualSid(psidSID,psidBuiltinDomainSid))
    { printf("\n%sSID is the Builtin Domain SID",ucIndentBuf);
    }
  }

  switch (peAcctNameUse)
  { case                      SidTypeUser           :
      printf("\n%sSID type is SidTypeUser"          ,ucIndentBuf);
      break;
    case                      SidTypeGroup          :
      printf("\n%sSID type is SidTypeGroup"         ,ucIndentBuf);
      break;
    case                      SidTypeDomain         :
      printf("\n%sSID type is SidTypeDomain"        ,ucIndentBuf);
      break;
    case                      SidTypeAlias          :
      printf("\n%sSID type is SidTypeAlias"         ,ucIndentBuf);
      break;
    case                      SidTypeWellKnownGroup :
      printf("\n%sSID type is SidTypeWellKnownGroup",ucIndentBuf);
      break;
    case                      SidTypeDeletedAccount :
      printf("\n%sSID type is SidTypeDeletedAccount",ucIndentBuf);
      break;
    case                      SidTypeInvalid        :
      printf("\n%sSID type is SidTypeInvalid"       ,ucIndentBuf);
      break;
    case                      SidTypeUnknown        :
      printf("\n%sSID type is SidTypeUnknown"       ,ucIndentBuf);
      break;
    default                   :
      printf("\n%sSID type is IMPOSSIBLE!!!!  Run debugger, see value!",ucIndentBuf);
      break;
  }
}

/****************************************************************************\
*
* FUNCTION: SIDStringName
*
\****************************************************************************/

BOOL SIDStringName(PSID psidSID, LPTSTR lpszSIDStringName)
{
  /**************************************************************************\
  *
  * Unfortunately there is no api to return the SID Revision, and the number
  *   of bytes in the Identifier Authority must be expressed as a define
  *   (since the == operator won't operate on structures so mempcy has to be
  *   used for the identifier authority compares)
  *
  \**************************************************************************/

  DWORD dwNumSubAuthorities;
  DWORD dwLen;
  DWORD dwSubAuthorityI;
  #define BytesInIdentifierAuthority  6
  SID_IDENTIFIER_AUTHORITY siaSidAuthority;
  SID_IDENTIFIER_AUTHORITY siaNullSidAuthority    = SECURITY_NULL_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY siaWorldSidAuthority   = SECURITY_WORLD_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY siaLocalSidAuthority   = SECURITY_LOCAL_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY siaCreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY siaNtAuthority         = SECURITY_NT_AUTHORITY;

  dwLen = sprintf(lpszSIDStringName,"S-%d-",SID_REVISION);

  if (SID_REVISION != ((PISID)psidSID)->Revision)
  { dwLen += sprintf(lpszSIDStringName+dwLen,"bad_revision==%d",((PISID)psidSID)->Revision);
  }

  siaSidAuthority = *(GetSidIdentifierAuthority(psidSID));

  if      (0==memcmp(&siaSidAuthority,&siaNullSidAuthority   ,BytesInIdentifierAuthority))
  { dwLen += sprintf(lpszSIDStringName+dwLen,"0");
  }
  else if (0==memcmp(&siaSidAuthority,&siaWorldSidAuthority  ,BytesInIdentifierAuthority))
  { dwLen += sprintf(lpszSIDStringName+dwLen,"1");
  }
  else if (0==memcmp(&siaSidAuthority,&siaLocalSidAuthority  ,BytesInIdentifierAuthority))
  { dwLen += sprintf(lpszSIDStringName+dwLen,"2");
  }
  else if (0==memcmp(&siaSidAuthority,&siaCreatorSidAuthority,BytesInIdentifierAuthority))
  { dwLen += sprintf(lpszSIDStringName+dwLen,"3");
  }
  else if (0==memcmp(&siaSidAuthority,&siaNtAuthority        ,BytesInIdentifierAuthority))
  { dwLen += sprintf(lpszSIDStringName+dwLen,"5");
  }
  else
  { dwLen += sprintf(lpszSIDStringName+dwLen,"UnknownAuthority!");
  }

  dwNumSubAuthorities = (DWORD)( *(GetSidSubAuthorityCount(psidSID)) );

  for (dwSubAuthorityI=0; dwSubAuthorityI<dwNumSubAuthorities; dwSubAuthorityI++)
  { dwLen += sprintf(lpszSIDStringName+dwLen,"-%d",*(GetSidSubAuthority(psidSID,dwSubAuthorityI)));
  }

  return(TRUE);
}

/****************************************************************************\
*
* FUNCTION: ExamineAccessToken
*
\****************************************************************************/

VOID ExamineAccessToken(HANDLE hAccessToken)
{ TOKEN_INFORMATION_CLASS ticInfoClass;
  #define                               SZ_TOK_INFO_BUF  2000
  UCHAR                   ucTokInfoBuf [SZ_TOK_INFO_BUF] = "";
  DWORD                   dwTokInfoBufSz;
  PTOKEN_USER             ptuTokenUser         = (PTOKEN_USER         )&ucTokInfoBuf;
  PTOKEN_GROUPS           ptgTokenGroups       = (PTOKEN_GROUPS       )&ucTokInfoBuf;
  PTOKEN_PRIVILEGES       ptpTokenPrivileges   = (PTOKEN_PRIVILEGES   )&ucTokInfoBuf;
  PTOKEN_OWNER            ptoTokenOwner        = (PTOKEN_OWNER        )&ucTokInfoBuf;
  PTOKEN_PRIMARY_GROUP    ptgTokenPrimaryGroup = (PTOKEN_PRIMARY_GROUP)&ucTokInfoBuf;
  PTOKEN_DEFAULT_DACL     ptdTokenDefaultDacl  = (PTOKEN_DEFAULT_DACL )&ucTokInfoBuf;
  PTOKEN_SOURCE           ptsTokenSource       = (PTOKEN_SOURCE       )&ucTokInfoBuf;
  PTOKEN_TYPE             pttTokenType         = (PTOKEN_TYPE         )&ucTokInfoBuf;
  PSECURITY_IMPERSONATION_LEVEL psilSecurityImpersonationLevel = (PSECURITY_IMPERSONATION_LEVEL)&ucTokInfoBuf;
  PTOKEN_STATISTICS       ptsTokenStatistics   = (PTOKEN_STATISTICS   )&ucTokInfoBuf;
  DWORD                   dwGroupI;
  DWORD                   dwPrivilegeI;
  #define                               SZ_PRIV_INFO_BUF  250
  UCHAR                   ucPrivInfoBuf[SZ_PRIV_INFO_BUF] = "";
  DWORD                   dwPrivInfoBufSz;
  DWORD                   dwExtraBits;
  UCHAR                   ucIndentBitsBuf[SZ_INDENT_BUF]  = "";

  strcpy(ucIndentBitsBuf,"");
  strcat(ucIndentBitsBuf,"                                  ");

  if    (!I_DO_NOT_WANT_THIS_CODE_TO_CLUTTER_THIS_PROGRAM_S_OUTPUT)
  {
    ticInfoClass   = TokenUser;
    dwTokInfoBufSz = SZ_TOK_INFO_BUF;

    if (!GetTokenInformation(hAccessToken,
                             ticInfoClass,
                             ucTokInfoBuf,
                             (DWORD)SZ_TOK_INFO_BUF,
                             &dwTokInfoBufSz))
    { PERR("GetTokenInformation");
      return;
    }

    printf("\nToken's User SID");
    printf("\n    (this is a SID that is used to compare to SIDs in DACL(s) and SACL(s)");

    if(!LookupSIDName( (*ptuTokenUser).User.Sid,""))
    { PERR("LookupSIDName failed");
    }

    printf("\nToken's User SID Attributes == 0x%.8x",(*ptuTokenUser).User.Attributes);
    printf("\n    These should always be 0 - see \\mstools\\h\\winnt.h right after");
    printf("\n      the defines such as SE_GROUP_LOGON_ID - there are no user");
    printf("\n      attributes yet defined");

    ticInfoClass   = TokenGroups;
    dwTokInfoBufSz = SZ_TOK_INFO_BUF;

    if (!GetTokenInformation(hAccessToken,
                             ticInfoClass,
                             ucTokInfoBuf,
                             (DWORD)SZ_TOK_INFO_BUF,
                             &dwTokInfoBufSz))
    { PERR("GetTokenInformation");
      return;
    }

    printf("\nToken groups (%d)",(*ptgTokenGroups).GroupCount);
    printf("\n    (these SID(s) also are used to compare to SIDs in DACL(s) and SACL(s)");

    for (dwGroupI=0; dwGroupI<(*ptgTokenGroups).GroupCount; dwGroupI++)
    {
      DWORD dwAttributeBits = (*ptgTokenGroups).Groups[dwGroupI].Attributes;
      printf("\n  Token group (%d)",dwGroupI);

      if(!LookupSIDName( (*ptgTokenGroups).Groups[dwGroupI].Sid,"  "))
      { PERR("LookupSIDName failed");
      }
      printf("\n  Token's group (%d) attributes == 0x%.8x",dwGroupI,dwAttributeBits);

      if (dwAttributeBits)
      {
        if ((dwAttributeBits   & SE_GROUP_MANDATORY         ) == SE_GROUP_MANDATORY         )
        { printf("\n%s0x00000001 SE_GROUP_MANDATORY         ",ucIndentBitsBuf);
        }
        if ((dwAttributeBits   & SE_GROUP_ENABLED_BY_DEFAULT) == SE_GROUP_ENABLED_BY_DEFAULT)
        { printf("\n%s0x00000002 SE_GROUP_ENABLED_BY_DEFAULT",ucIndentBitsBuf);
        }
        if ((dwAttributeBits   & SE_GROUP_ENABLED           ) == SE_GROUP_ENABLED           )
        { printf("\n%s0x00000004 SE_GROUP_ENABLED           ",ucIndentBitsBuf);
        }
        if ((dwAttributeBits   & SE_GROUP_OWNER             ) == SE_GROUP_OWNER             )
        { printf("\n%s0x00000008 SE_GROUP_OWNER             ",ucIndentBitsBuf);
        }
        if ((dwAttributeBits   & SE_GROUP_LOGON_ID          ) == SE_GROUP_LOGON_ID          )
        { printf("\n%s0xC0000000 SE_GROUP_LOGON_ID          ",ucIndentBitsBuf);
        }

        dwExtraBits = dwAttributeBits & ( ~( SE_GROUP_MANDATORY
                                           | SE_GROUP_ENABLED_BY_DEFAULT
                                           | SE_GROUP_ENABLED
                                           | SE_GROUP_OWNER
                                           | SE_GROUP_LOGON_ID) );
        if (0 != dwExtraBits)
        { printf("\n          Extra attribute bits == 0x%.8x <-This is a problem, should be all 0s",dwExtraBits);
        }
      }
    }

    ticInfoClass   = TokenPrivileges;
    dwTokInfoBufSz = SZ_TOK_INFO_BUF;

    if (!GetTokenInformation(hAccessToken,
                             ticInfoClass,
                             ucTokInfoBuf,
                             (DWORD)SZ_TOK_INFO_BUF,
                             &dwTokInfoBufSz))
    { PERR("GetTokenInformation");
      return;
    }

    printf("\nToken privileges (%d)",(*ptpTokenPrivileges).PrivilegeCount);
    printf("\n  NOTE: Most token privileges are not enabled by default.");
    printf("\n    For example the privilege to reboot or logoff is not.");
    printf("\n    0x00000000 for attributes implies the privilege is not enabled.");
    printf("\n    Use care when enabling privileges.  Enable only those needed,");
    printf("\n      and leave them enabled only for as long as they are needed.");

    for (dwPrivilegeI=0; dwPrivilegeI<(*ptpTokenPrivileges).PrivilegeCount; dwPrivilegeI++)
    {
      LUID  luidTokenLuid   = (*ptpTokenPrivileges).Privileges[dwPrivilegeI].Luid;
      DWORD dwAttributeBits = (*ptpTokenPrivileges).Privileges[dwPrivilegeI].Attributes;

      dwPrivInfoBufSz = SZ_PRIV_INFO_BUF;

      if (!LookupPrivilegeName(NULL,
                               (PLUID)&luidTokenLuid,
                               (LPTSTR)ucPrivInfoBuf,
                               (LPDWORD)&dwPrivInfoBufSz))
      { PERR("LookUpPrivilegeName");
        return;
      }

      printf("\n  Token's privilege (%.2d) name       == %s",dwPrivilegeI,ucPrivInfoBuf);

      printf("\n  Token's privilege (%.2d) attributes == 0x%.8x",dwPrivilegeI,dwAttributeBits);

      if (dwAttributeBits)
      {
        if ((dwAttributeBits   & SE_PRIVILEGE_ENABLED_BY_DEFAULT) == SE_PRIVILEGE_ENABLED_BY_DEFAULT)
        { printf("\n%s     0x00000001 SE_PRIVILEGE_ENABLED_BY_DEFAULT",ucIndentBitsBuf);
        }
        if ((dwAttributeBits   & SE_PRIVILEGE_ENABLED           ) == SE_PRIVILEGE_ENABLED           )
        { printf("\n%s     0x00000002 SE_PRIVILEGE_ENABLED           ",ucIndentBitsBuf);
        }
        if ((dwAttributeBits   & SE_PRIVILEGE_USED_FOR_ACCESS   ) == SE_PRIVILEGE_USED_FOR_ACCESS   )
        { printf("\n%s     0x80000000 SE_PRIVILEGE_USED_FOR_ACCESS   ",ucIndentBitsBuf);
        }

        dwExtraBits = dwAttributeBits & ( ~( SE_PRIVILEGE_ENABLED_BY_DEFAULT
                                           | SE_PRIVILEGE_ENABLED
                                           | SE_PRIVILEGE_USED_FOR_ACCESS ) );
        if (0 != dwExtraBits)
        { printf("\n               Extra attribute bits == 0x%.8x <-This is a problem, should be all 0s",dwExtraBits);
        }
      }
    }

    ticInfoClass   = TokenOwner;
    dwTokInfoBufSz = SZ_TOK_INFO_BUF;

    if (!GetTokenInformation(hAccessToken,
                             ticInfoClass,
                             ucTokInfoBuf,
                             (DWORD)SZ_TOK_INFO_BUF,
                             &dwTokInfoBufSz))
    { PERR("GetTokenInformation");
      return;
    }

    printf("\nToken's default-owner-SID for created objects");
    printf("\n    (this is NOT a SID that is used to compare to SIDs in DACL(s) and SACL(s)");

    if(!LookupSIDName((*ptoTokenOwner).Owner,""))
    { PERR("LookupSIDName failed");
    }

    ticInfoClass   = TokenPrimaryGroup;
    dwTokInfoBufSz = SZ_TOK_INFO_BUF;

    if (!GetTokenInformation(hAccessToken,
                             ticInfoClass,
                             ucTokInfoBuf,
                             (DWORD)SZ_TOK_INFO_BUF,
                             &dwTokInfoBufSz))
    { PERR("GetTokenInformation");
      return;
    }

    printf("\nToken's Primary Group SID");
    printf("\n    (Current uses are Posix and Macintosh client support)");

    if(!LookupSIDName((*ptgTokenPrimaryGroup).PrimaryGroup,""))
    { PERR("LookupSIDName failed");
    }

    ticInfoClass   = TokenDefaultDacl;
    dwTokInfoBufSz = SZ_TOK_INFO_BUF;

    if (!GetTokenInformation(hAccessToken,
                             ticInfoClass,
                             ucTokInfoBuf,
                             (DWORD)SZ_TOK_INFO_BUF,
                             &dwTokInfoBufSz))
    { PERR("GetTokenInformation");
      return;
    }

    if (NULL         == (*ptdTokenDefaultDacl).DefaultDacl)
    { printf("\nToken has a NULL Default DACL explicitly specified (allows all access to");
      printf("\n    Everyone, only on objects that are created where the object's Dacl is");
      printf("\n    assigned by default from this default Dacl in this access token)");
    }
    else
    { printf("\nToken's default-DACL for created objects");
      if(!ExamineACL((*ptdTokenDefaultDacl).DefaultDacl,"",DefaultDaclInAccessTokenAccessMask))
      { PERR("ExamineACL failed");
      }
    }

    ticInfoClass   = TokenSource;
    dwTokInfoBufSz = SZ_TOK_INFO_BUF;

    if (!GetTokenInformation(hAccessToken,
                             ticInfoClass,
                             ucTokInfoBuf,
                             (DWORD)SZ_TOK_INFO_BUF,
                             &dwTokInfoBufSz))
    { PERR("GetTokenInformation");
      return;
    }

    printf("\nToken's Source");
    printf("\n  Source Name        == %.8s",(*ptsTokenSource).SourceName);
    printf("\n  Source Identifier  == 0x%.8x%.8x",
       (*ptsTokenSource).SourceIdentifier.HighPart,
       (*ptsTokenSource).SourceIdentifier.LowPart);

    ticInfoClass   = TokenType;
    dwTokInfoBufSz = SZ_TOK_INFO_BUF;

    if (!GetTokenInformation(hAccessToken,
                             ticInfoClass,
                             ucTokInfoBuf,
                             (DWORD)SZ_TOK_INFO_BUF,
                             &dwTokInfoBufSz))
    { PERR("GetTokenInformation");
      return;
    }

    switch (*pttTokenType)
    { case  TokenPrimary       :
        printf("\nToken's Type is TokenPrimary");
        break;
      case  TokenImpersonation :
        printf("\nToken's Type is TokenImpersonation");
        printf("\n    Hence the token's TokenImpersonationLevel can be examined");

        ticInfoClass   = TokenImpersonationLevel;
        dwTokInfoBufSz = SZ_TOK_INFO_BUF;

        if (!GetTokenInformation(hAccessToken,
                                 ticInfoClass,
                                 ucTokInfoBuf,
                                 (DWORD)SZ_TOK_INFO_BUF,
                                 &dwTokInfoBufSz))
        { PERR("GetTokenInformation");
          return;
        }

        switch (*psilSecurityImpersonationLevel)
        { case                     SecurityAnonymous      :
            printf("\n  Token is a SecurityAnonymous impersonation token");
            break;
          case                     SecurityIdentification :
            printf("\n  Token is a SecurityIdentification impersonation token");
            break;
          case                     SecurityImpersonation  :
            printf("\n  Token is a SecurityImpersonation impersonation token");
            break;
          case                     SecurityDelegation     :
            printf("\n  Token is a SecurityDelegation impersonation token");
            break;
          default                   :
            printf("\n  Token is an ILLEGAL KIND OF impersonation token!!! == 0x%.8x",*psilSecurityImpersonationLevel);
            break;
        }

      default                  :
        printf("\nToken's Type is ILLEGAL!!! == 0x%.8x",*pttTokenType);
        break;
    }

    ticInfoClass   = TokenStatistics;
    dwTokInfoBufSz = SZ_TOK_INFO_BUF;

    if (!GetTokenInformation(hAccessToken,
                             ticInfoClass,
                             ucTokInfoBuf,
                             (DWORD)SZ_TOK_INFO_BUF,
                             &dwTokInfoBufSz))
    { PERR("GetTokenInformation");
      return;
    }

    printf("\nToken's Statistics");
    printf("\n  TokenId            == 0x%.8x%.8x",
      (*ptsTokenStatistics).TokenId.HighPart,
      (*ptsTokenStatistics).TokenId.LowPart);
    printf("\n  AuthenticationId   == 0x%.8x%.8x",
      (*ptsTokenStatistics).AuthenticationId.HighPart,
      (*ptsTokenStatistics).AuthenticationId.LowPart);
    printf("\n  ExpirationTime     == (not supported in this release of Windows NT)");
    printf("\n  TokenType          == See token type above");
    printf("\n  ImpersonationLevel == See impersonation level above (only if TokenType is not TokenPrimary)");
    printf("\n  DynamicCharged     == %ld",(*ptsTokenStatistics).DynamicCharged    );
    printf("\n  DynamicAvailable   == %ld",(*ptsTokenStatistics).DynamicAvailable  );
    printf("\n  GroupCount         == %d",(*ptsTokenStatistics).GroupCount        );
    printf("\n  PrivilegeCount     == %d",(*ptsTokenStatistics).PrivilegeCount    );
    printf("\n  ModifiedId         == 0x%.8x%.8x",
      (*ptsTokenStatistics).ModifiedId.HighPart,
      (*ptsTokenStatistics).ModifiedId.LowPart);

    printf("\n\n");

  }
}

/****************************************************************************\
*
* FUNCTION: SetPrivilegeInAccessToken
*
\****************************************************************************/

BOOL SetPrivilegeInAccessToken(VOID)
{
  HANDLE           hProcess;
  HANDLE           hAccessToken;
  LUID             luidPrivilegeLUID;
  TOKEN_PRIVILEGES tpTokenPrivilege;

  hProcess = GetCurrentProcess();
  if (!hProcess)
  { PERR("GetCurrentProcess");
    return(FALSE);
  }

  if (!OpenProcessToken(hProcess,
                        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                        &hAccessToken))
  { PERR("OpenProcessToken");
    return(FALSE);
  }

  /**************************************************************************\
  *
  * Get LUID of SeSecurityPrivilege privilege
  *
  \**************************************************************************/

  if (!LookupPrivilegeValue(NULL,
                            SE_SECURITY_NAME,
                            &luidPrivilegeLUID))
  { PERR("LookupPrivilegeValue");
    printf("\nThe above error means you need to log on as an Administrator");
    return(FALSE);
  }

  /**************************************************************************\
  *
  * Enable the SeSecurityPrivilege privilege using the LUID just
  *   obtained
  *
  \**************************************************************************/

  tpTokenPrivilege.PrivilegeCount = 1;
  tpTokenPrivilege.Privileges[0].Luid = luidPrivilegeLUID;
  tpTokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

  AdjustTokenPrivileges (hAccessToken,
                         FALSE,  // Do not disable all
                         &tpTokenPrivilege,
                         sizeof(TOKEN_PRIVILEGES),
                         NULL,   // Ignore previous info
                         NULL);  // Ignore previous info

  if ( GetLastError() != NO_ERROR )
  { PERR("AdjustTokenPrivileges");
    return(FALSE);
  }

  return(TRUE);
}

/****************************************************************************\
*
* FUNCTION: InitializeWellKnownSIDs
*
\****************************************************************************/

VOID InitializeWellKnownSIDs(VOID)
{
  DWORD dwSidWith0SubAuthorities;
  DWORD dwSidWith1SubAuthority;
  DWORD dwSidWith2SubAuthorities;
  DWORD dwSidWith3SubAuthorities;
  DWORD dwSidWith4SubAuthorities;

  SID_IDENTIFIER_AUTHORITY siaNullSidAuthority    = SECURITY_NULL_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY siaWorldSidAuthority   = SECURITY_WORLD_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY siaLocalSidAuthority   = SECURITY_LOCAL_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY siaCreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY siaNtAuthority         = SECURITY_NT_AUTHORITY;

  //  These SID sizes need to be allocated

  dwSidWith0SubAuthorities = GetSidLengthRequired( 0 );
  dwSidWith1SubAuthority   = GetSidLengthRequired( 1 );
  dwSidWith2SubAuthorities = GetSidLengthRequired( 2 );
  dwSidWith3SubAuthorities = GetSidLengthRequired( 3 );
  dwSidWith4SubAuthorities = GetSidLengthRequired( 4 );

  //  Allocate and initialize the universal SIDs

  psidNullSid         = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);
  psidWorldSid        = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);
  psidLocalSid        = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);
  psidCreatorOwnerSid = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);
  psidCreatorGroupSid = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);

  InitializeSid( psidNullSid,         &siaNullSidAuthority,    1 );
  InitializeSid( psidWorldSid,        &siaWorldSidAuthority,   1 );
  InitializeSid( psidLocalSid,        &siaLocalSidAuthority,   1 );
  InitializeSid( psidCreatorOwnerSid, &siaCreatorSidAuthority, 1 );
  InitializeSid( psidCreatorGroupSid, &siaCreatorSidAuthority, 1 );

  *(GetSidSubAuthority( psidNullSid,         0 )) = SECURITY_NULL_RID;
  *(GetSidSubAuthority( psidWorldSid,        0 )) = SECURITY_WORLD_RID;
  *(GetSidSubAuthority( psidLocalSid,        0 )) = SECURITY_LOCAL_RID;
  *(GetSidSubAuthority( psidCreatorOwnerSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
  *(GetSidSubAuthority( psidCreatorGroupSid, 0 )) = SECURITY_CREATOR_GROUP_RID;

  // Allocate and initialize the NT defined SIDs

  psidNtAuthoritySid   = (PSID)LocalAlloc(LPTR,dwSidWith0SubAuthorities);
  psidDialupSid        = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);
  psidNetworkSid       = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);
  psidBatchSid         = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);
  psidInteractiveSid   = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);
  psidLogonIdsSid      = (PSID)LocalAlloc(LPTR,dwSidWith3SubAuthorities);
  psidServiceSid       = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);
  psidLocalSystemSid   = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);
  psidBuiltinDomainSid = (PSID)LocalAlloc(LPTR,dwSidWith1SubAuthority);

  InitializeSid( psidNtAuthoritySid,   &siaNtAuthority, 0 );
  InitializeSid( psidDialupSid,        &siaNtAuthority, 1 );
  InitializeSid( psidNetworkSid,       &siaNtAuthority, 1 );
  InitializeSid( psidBatchSid,         &siaNtAuthority, 1 );
  InitializeSid( psidInteractiveSid,   &siaNtAuthority, 1 );
  InitializeSid( psidLogonIdsSid,      &siaNtAuthority, 3 );
  InitializeSid( psidServiceSid,       &siaNtAuthority, 1 );
  InitializeSid( psidLocalSystemSid,   &siaNtAuthority, 1 );
  InitializeSid( psidBuiltinDomainSid, &siaNtAuthority, 1 );

  *(GetSidSubAuthority( psidDialupSid,        0 )) = SECURITY_DIALUP_RID;
  *(GetSidSubAuthority( psidNetworkSid,       0 )) = SECURITY_NETWORK_RID;
  *(GetSidSubAuthority( psidBatchSid,         0 )) = SECURITY_BATCH_RID;
  *(GetSidSubAuthority( psidInteractiveSid,   0 )) = SECURITY_INTERACTIVE_RID;
  *(GetSidSubAuthority( psidLogonIdsSid,      0 )) = SECURITY_LOGON_IDS_RID;
  *(GetSidSubAuthority( psidLogonIdsSid,      1 )) = 0; // Bogus!
  *(GetSidSubAuthority( psidLogonIdsSid,      2 )) = 0; // Also bogus!
  *(GetSidSubAuthority( psidServiceSid,       0 )) = SECURITY_SERVICE_RID;
  *(GetSidSubAuthority( psidLocalSystemSid,   0 )) = SECURITY_LOCAL_SYSTEM_RID;
  *(GetSidSubAuthority( psidBuiltinDomainSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
}

/****************************************************************************\
*
* FUNCTION: DisplayHelp
*
\****************************************************************************/

VOID DisplayHelp(VOID)
{
  printf("\nTo run type CHECK_SD and 0 or 1 parameters.  Syntax:");
  printf("\n  CHECK_SD");
  printf("\n      or");
  printf("\n  CHECK_SD filename");
  printf("\n           filename is the name of the file that is passed");
  printf("\n             to GetFileSecurity() to fetch the SD to examine");
  printf("\nExamples:");
  printf("\n  CHECK_SD            Checks the SD on A: (this is the default)");
  printf("\n  CHECK_SD \\\\.\\A:     Checks the SD on A:");
  printf("\n  CHECK_SD d:\\a.fil   Checks the SD on d:\a.fil");
  printf("\n  CHECK_SD A:         Checks the SD on the A: root, but that");
  printf("\n                        is not where the DACL is that controls");
  printf("\n                        access to the floppy, so don't do this");
}


DISPTRUS.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    disptrus.c

Abstract:

    This sample enumerates the trusted domains on the target machine, and
    displays them on stdout.  This list matches (minus sorting) the list of
    domains presents by the WinNT Dacl editor.  For practical applications,
    replace the AddTrustToList() function with linked-list management code
    and then use the resultant list later on in the application.

    The optional target machine is specified as argv[1], eg, lsatrust \\winbase

    For a WinNT workstation which is not a member of a domain, the only
    relevant trusted domain is the account domain of the machine.  We detect
    a non-member workstation by checking if the primary domain Sid is NULL.

    For a WinNT workstation which is a member of a domain, the relevant trusted
    domain is the account domain, the primary domain, and the trusted domain(s)
    of the primary domain.

    For a WinNT Domain controller, the account domain and the trusted domains.

Author:

    Scott Field (sfield) 16-Mar-96

--*/

#include <windows.h>
#include <lm.h>
#include "ntsecapi.h" // \mstools\samples\win32\winnt\security\include\ntsecapi.h

#include <stdio.h>

BOOL
BuildTrustList(
    LPWSTR Target
    );

BOOL
IsDomainController(
    LPWSTR Server,
    LPBOOL bDomainController
    );

BOOL
EnumTrustedDomains(
    LSA_HANDLE PolicyHandle
    );

BOOL
AddTrustToList(
    PLSA_UNICODE_STRING UnicodeString
    );

//
// helper functions
//

void
InitLsaString(
    PLSA_UNICODE_STRING LsaString,
    LPWSTR String
    );

NTSTATUS
OpenPolicy(
    LPWSTR ServerName,
    DWORD DesiredAccess,
    PLSA_HANDLE PolicyHandle
    );

void
DisplayWinError(
    LPSTR szAPI,        // pointer to Ansi function name
    DWORD dwLastError   // DWORD WinError
    );

#define RTN_OK 0
#define RTN_ERROR 13

//
// if you have the ddk, include ntstatus.h
//
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS                  ((NTSTATUS)0x00000000L)
#define STATUS_MORE_ENTRIES             ((NTSTATUS)0x00000105L)
#define STATUS_NO_MORE_ENTRIES          ((NTSTATUS)0x8000001AL)
#endif

#define ELEMENT_COUNT 64    // number of array elements to allocate

LPWSTR *g_TrustList;        // array of trust elements
DWORD g_TrustCount;         // number of elements in g_TrustList

//
// Unicode entry point and argv
//

int
__cdecl
wmain(
    int argc,
    wchar_t *argv[]
    )
{
    LPWSTR TargetComputer;
    DWORD i;

    g_TrustCount = 0;
    g_TrustList = (LPWSTR *)HeapAlloc(
        GetProcessHeap(), HEAP_ZERO_MEMORY,
        ELEMENT_COUNT * sizeof(LPWSTR)
        );

    if(g_TrustList == NULL) {
        printf("HeapAlloc error!\n");
        return RTN_ERROR;
    }

    //
    // if a target computer was specified, get it
    //
    if(argc == 2)
        TargetComputer = argv[1];
    else
        TargetComputer = NULL; // default to local machine

    //
    // build the trust list, and display it if successful
    //
    if(!BuildTrustList(TargetComputer)) {
        DisplayWinError("BuildTrustList", GetLastError());
    } else {

        //
        // display trust list
        //
        for(i = 0 ; i < g_TrustCount ; i++)
            printf("%ls\n", g_TrustList[i]);
    }

    //
    // free trust list
    //
    for(i = 0 ; i < g_TrustCount ; i++) {
        if(g_TrustList[i] != NULL)
            HeapFree(GetProcessHeap(), 0, g_TrustList[i]);
    }

    HeapFree(GetProcessHeap(), 0, g_TrustList);

    return RTN_OK;
}

BOOL
BuildTrustList(
    LPWSTR Target
    )
{
    LSA_HANDLE PolicyHandle;
    NTSTATUS Status;

    PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomain;
    BOOL bDC;
    NET_API_STATUS nas = NERR_Success; // assume success

    BOOL bSuccess = FALSE; // assume this function will fail

    //
    // open the policy on the specified machine
    //
    Status = OpenPolicy(
                Target,
                POLICY_VIEW_LOCAL_INFORMATION,
                &PolicyHandle
                );

    if(Status != STATUS_SUCCESS) {
        SetLastError( LsaNtStatusToWinError(Status) );
        return FALSE;
    }

    //
    // obtain the AccountDomain, which is common to all three cases
    //
    Status = LsaQueryInformationPolicy(
                PolicyHandle,
                PolicyAccountDomainInformation,
                &AccountDomain
                );

    if(Status != STATUS_SUCCESS)
        goto cleanup;

    //
    // Note: AccountDomain->DomainSid will contain binary Sid
    //
    AddTrustToList(&AccountDomain->DomainName);

    //
    // free memory allocated for account domain
    //
    LsaFreeMemory(AccountDomain);

    //
    // find out if the target machine is a domain controller
    //
    if(!IsDomainController(Target, &bDC)) {
        ////
        goto cleanup;
    }

    if(!bDC) {
        PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomain;
        LPWSTR szPrimaryDomainName = NULL;
        LPWSTR DomainController = NULL;

        //
        // get the primary domain
        //
        Status = LsaQueryInformationPolicy(
                PolicyHandle,
                PolicyPrimaryDomainInformation,
                &PrimaryDomain
                );

        if(Status != STATUS_SUCCESS)
            goto cleanup;

        //
        // if the primary domain Sid is NULL, we are a non-member, and
        // our work is done.
        //
        if(PrimaryDomain->Sid == NULL) {
            LsaFreeMemory(PrimaryDomain);
            bSuccess = TRUE;
            goto cleanup;
        }

        AddTrustToList(&PrimaryDomain->Name);

        //
        // build a copy of what we just added.  This is necessary in order
        // to lookup the domain controller for the specified domain.
        // the Domain name must be NULL terminated for NetGetDCName(),
        // and the LSA_UNICODE_STRING buffer is not necessarilly NULL
        // terminated.  Note that in a practical implementation, we
        // could just extract the element we added, since it ends up
        // NULL terminated.
        //

        szPrimaryDomainName = (LPWSTR)HeapAlloc(
            GetProcessHeap(), 0,
            PrimaryDomain->Name.Length + sizeof(WCHAR) // existing length + NULL
            );

        if(szPrimaryDomainName != NULL) {
            //
            // copy the existing buffer to the new storage, appending a NULL
            //
            lstrcpynW(
                szPrimaryDomainName,
                PrimaryDomain->Name.Buffer,
                (PrimaryDomain->Name.Length / sizeof(WCHAR)) + 1
                );
        }

        LsaFreeMemory(PrimaryDomain);

        if(szPrimaryDomainName == NULL) goto cleanup;

        //
        // get the primary domain controller computer name
        //
        nas = NetGetDCName(
            NULL,
            szPrimaryDomainName,
            (LPBYTE *)&DomainController
            );

        HeapFree(GetProcessHeap(), 0, szPrimaryDomainName);

        if(nas != NERR_Success)
            goto cleanup;

        //
        // close the policy handle, because we don't need it anymore
        // for the workstation case, as we open a handle to a DC
        // policy below
        //
        LsaClose(PolicyHandle);
        PolicyHandle = INVALID_HANDLE_VALUE; // invalidate handle value

        //
        // open the policy on the domain controller
        //
        Status = OpenPolicy(
                    DomainController,
                    POLICY_VIEW_LOCAL_INFORMATION,
                    &PolicyHandle
                    );

        //
        // free the domaincontroller buffer
        //
        NetApiBufferFree(DomainController);

        if(Status != STATUS_SUCCESS)
            goto cleanup;
    }

    //
    // build additional trusted domain(s) list and indicate if successful
    //
    bSuccess = EnumTrustedDomains(PolicyHandle);

cleanup:

    //
    // close the policy handle
    //
    if(PolicyHandle != INVALID_HANDLE_VALUE)
        LsaClose(PolicyHandle);

    if(!bSuccess) {
        if(Status != STATUS_SUCCESS)
            SetLastError( LsaNtStatusToWinError(Status) );
        else if(nas != NERR_Success)
            SetLastError( nas );
    }

    return bSuccess;
}

BOOL
EnumTrustedDomains(
    LSA_HANDLE PolicyHandle
    )
{
    LSA_ENUMERATION_HANDLE lsaEnumHandle=0; // start an enum
    PLSA_TRUST_INFORMATION TrustInfo;
    ULONG ulReturned;               // number of items returned
    ULONG ulCounter;                // counter for items returned
    NTSTATUS Status;

    do {
        Status = LsaEnumerateTrustedDomains(
                        PolicyHandle,   // open policy handle
                        &lsaEnumHandle, // enumeration tracker
                        &TrustInfo,     // buffer to receive data
                        32000,          // recommended buffer size
                        &ulReturned     // number of items returned
                        );
        //
        // get out if an error occurred
        //
        if( (Status != STATUS_SUCCESS) &&
            (Status != STATUS_MORE_ENTRIES) &&
            (Status != STATUS_NO_MORE_ENTRIES)
            ) {
            SetLastError( LsaNtStatusToWinError(Status) );
            return FALSE;
        }

        //
        // Display results
        // Note: Sids are in TrustInfo[ulCounter].Sid
        //
        for(ulCounter = 0 ; ulCounter < ulReturned ; ulCounter++)
            AddTrustToList(&TrustInfo[ulCounter].Name);

        //
        // free the buffer
        //
        LsaFreeMemory(TrustInfo);

    } while (Status != STATUS_NO_MORE_ENTRIES);

    return TRUE;
}

BOOL
IsDomainController(
    LPWSTR Server,
    LPBOOL bDomainController
    )
{
    PSERVER_INFO_101 si101;
    NET_API_STATUS nas;

    nas = NetServerGetInfo(
        (LPTSTR)Server,
        101,    // info-level
        (LPBYTE *)&si101
        );

    if(nas != NERR_Success) {
        SetLastError(nas);
        return FALSE;
    }

    if( (si101->sv101_type & SV_TYPE_DOMAIN_CTRL) ||
        (si101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL) ) {
        //
        // we are dealing with a DC
        //
        *bDomainController = TRUE;
    } else {
        *bDomainController = FALSE;
    }

    NetApiBufferFree(si101);

    return TRUE;
}

BOOL
AddTrustToList(
    PLSA_UNICODE_STRING UnicodeString
    )
{
    if(g_TrustCount >= ELEMENT_COUNT) return FALSE;

    //
    // allocate storage for array element
    //
    g_TrustList[g_TrustCount] = (LPWSTR)HeapAlloc(
        GetProcessHeap(), 0,
        UnicodeString->Length + sizeof(WCHAR) // existing length + NULL
        );

    if(g_TrustList[g_TrustCount] == NULL) return FALSE;

    //
    // copy the existing buffer to the new storage, appending a NULL
    //
    lstrcpynW(
        g_TrustList[g_TrustCount],
        UnicodeString->Buffer,
        (UnicodeString->Length / sizeof(WCHAR)) + 1
        );

    g_TrustCount++; // increment the trust count

    return TRUE;
}

void
InitLsaString(
    PLSA_UNICODE_STRING LsaString,
    LPWSTR String
    )
{
    DWORD StringLength;

    if (String == NULL) {
        LsaString->Buffer = NULL;
        LsaString->Length = 0;
        LsaString->MaximumLength = 0;

        return;
    }

    StringLength = lstrlenW(String);
    LsaString->Buffer = String;
    LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
    LsaString->MaximumLength = (USHORT) (StringLength + 1) * sizeof(WCHAR);
}

NTSTATUS
OpenPolicy(
    LPWSTR ServerName,
    DWORD DesiredAccess,
    PLSA_HANDLE PolicyHandle
    )
{
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    LSA_UNICODE_STRING ServerString;
    PLSA_UNICODE_STRING Server;

    //
    // Always initialize the object attributes to all zeroes
    //
    ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

    if(ServerName != NULL) {
        //
        // Make a LSA_UNICODE_STRING out of the LPWSTR passed in
        //
        InitLsaString(&ServerString, ServerName);

        Server = &ServerString;
    } else {
        Server = NULL;
    }

    //
    // Attempt to open the policy
    //
    return LsaOpenPolicy(
                Server,
                &ObjectAttributes,
                DesiredAccess,
                PolicyHandle
                );
}

void
DisplayWinError(
    LPSTR szAPI,    // pointer to failed API name
    DWORD dwLastError
    )
{
    HMODULE hModule = NULL;
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    fprintf(stderr,"%s error! (rc=%lu)\n", szAPI, dwLastError);

    if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
        hModule = LoadLibraryEx(
            TEXT("netmsg.dll"),
            NULL,
            LOAD_LIBRARY_AS_DATAFILE
            );
    }

    if(dwBufferLength=FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_IGNORE_INSERTS |
        FORMAT_MESSAGE_FROM_SYSTEM |
        ((hModule != NULL) ? FORMAT_MESSAGE_FROM_HMODULE : 0),
        hModule, // module to get message from
        dwLastError,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
        (LPSTR) &MessageBuffer,
        0,
        NULL
        ))
    {
        DWORD dwBytesWritten;

        //
        // Output message string on stderr
        //
        WriteFile(
            GetStdHandle(STD_ERROR_HANDLE),
            MessageBuffer,
            dwBufferLength,
            &dwBytesWritten,
            NULL
            );

        //
        // free the buffer allocated by the system
        //
        LocalFree(MessageBuffer);
    }

    if(hModule != NULL)
        FreeLibrary(hModule);
}


DOMTRUST.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    domtrust.c

Abstract:

    This sample illustrates how to manage Windows NT trusted domains at the
    domain controller level.

    The first command line argument indicates the name of the new or existing
    trusted domain to create or modify.

    The second command line argument indicates the new password for the
    trusted domain specified in the first argument.

    The optional third argument indicates the domain name that is the target
    of the trusted domain update operation. If this argument is not specified,
    the update will occur on the local domain. Note that this sample will not
    allow a trusted domain update to occur on a non-domain controller.

    This sample works correctly compiled ANSI or Unicode. Note that LAN
    Manager NetXxx API are Unicode only, and Windows NT LSA API are Unicode
    only.

Author:

    Scott Field (sfield)    05-Feb-96

--*/

#ifndef UNICODE
#define UNICODE
#define _UNICODE
#endif

#include <windows.h>
#include <lm.h>         // for NetXxx API

#include "ntsecapi.h"   // \mstools\samples\win32\winnt\security\include\ntsecapi.h

#include <stdio.h>

void
InitLsaString(
    PLSA_UNICODE_STRING LsaString,
    LPWSTR String
    );

NTSTATUS
OpenPolicy(
    LPWSTR ServerName,
    DWORD DesiredAccess,
    PLSA_HANDLE PolicyHandle
    );

BOOL
GetDomainSid(
    LPWSTR DomainName,  // domain name to acquire Sid of
    PSID *pDomainSid    // points to allocated Sid on success
    );

NTSTATUS
SetTrustedDomainInfo(
    LSA_HANDLE PolicyHandle,
    PSID DomainSid,             // Sid of domain to manipulate
    LPWSTR TrustedDomainName,   // trusted domain name to add/update
    LPWSTR Password             // new trust password for trusted domain
    );

BOOL
VerifyTrustRelationship(
    LPWSTR TargetDomainName,        // domain name to verify trust at
    LPWSTR TrustAccountToVerify,    // trusted domain name to verify
    LPWSTR Password,                // password associated with trust
    LPBOOL bTrustVerified           // indicates if trust was verified
    );

void
DisplayNtStatus(
    LPSTR szAPI,    // ANSI string containing API name
    NTSTATUS Status
    );

void
DisplayError(
    LPSTR szAPI,    // pointer to failed API name
    DWORD dwLastError
    );

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

//
// if you have the DDK, include Ntstatus.h
//
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS                  ((NTSTATUS)0x00000000L)
#define STATUS_OBJECT_NAME_NOT_FOUND    ((NTSTATUS)0xC0000034L)
#define STATUS_INVALID_SID              ((NTSTATUS)0xC0000078L)
#endif

//
// Unicode entry point and argv
//

int
__cdecl
wmain(
    int argc,
    wchar_t *argv[]
    )
{
    BOOL bAllocTargetDomain;    // did you allocate memory for target?
    LSA_HANDLE PolicyHandle;
    LPWSTR TargetDomainName;    // target domain of policy update
    LPWSTR DomainController;    // computer name where policy update
    PSID DomainSid; // allocated Sid representing domain to trust
    LPWSTR TrustedDomainName;
    LPWSTR Password;
    BOOL bTrustVerified;
    NET_API_STATUS nas;
    NTSTATUS Status;

    if(argc < 3) {
        fprintf(stderr,
            "Usage: %ls <NewTrustedDomain> <Password> [TargetDomain]\n",
            argv[0]);
        return RTN_USAGE;
    }

    TrustedDomainName = argv[1];    // existing or new TrustedDomain
    Password = argv[2];             // new password for TrustedDomain

    //
    // if a TargetDomain was specified, point to that.
    // if not, set TargetDomain to the current domain if a DC.
    //
    if(argc == 4 && *argv[3] != L'\0') {
        TargetDomainName = argv[3]; // domain to update trust at
        bAllocTargetDomain = FALSE; // no memory allocated
    }
    else {
        PSERVER_INFO_101 si101;
        DWORD Type;
        PUSER_MODALS_INFO_2 umi2;

        //
        // ensure that the local computer is a DC. This operation is only
        // appropriate against a domain controller.
        //
        nas = NetServerGetInfo(NULL, 101, (LPBYTE *)&si101);
        if(nas != NERR_Success) {
            DisplayError("NetServerGetInfo", nas);
            return RTN_ERROR;
        }

        Type = si101->sv101_type;
        NetApiBufferFree(si101);

        if( !(Type & SV_TYPE_DOMAIN_CTRL) &&
            !(Type & SV_TYPE_DOMAIN_BAKCTRL) ) {
            printf("Error:  Specify a TargetDomain; this operation"
                    " is only valid against a domain.\n");
            return RTN_ERROR;
        }

        //
        // obtain the local computer's domain name
        //
        nas = NetUserModalsGet(NULL, 2, (LPBYTE *)&umi2);

        if(nas != NERR_Success) {
            DisplayError("NetUserModalsGet", nas);
            return RTN_ERROR;
        }

        //
        // copy the domain name to new storage
        //
        TargetDomainName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
            (lstrlenW(umi2->usrmod2_domain_name) + 1) * sizeof(WCHAR));

        if(TargetDomainName != NULL) {
            lstrcpyW(TargetDomainName, umi2->usrmod2_domain_name);
            bAllocTargetDomain = TRUE; // we allocated memory
        }

        NetApiBufferFree(umi2); // free memory allocated by NetXxx

        //
        // if an error occurred allocating memory, exit
        //
        if(TargetDomainName == NULL) {
            DisplayError("HeapAlloc", 0);
            return RTN_ERROR;
        }
    }

    //
    // do not allow a Domain to trust itself
    //
    if(lstrcmpiW(TargetDomainName, TrustedDomainName) == 0) {
        fprintf(stderr,"Error:  Domain %ls cannot trust itself.\n",
            TargetDomainName);

        return RTN_ERROR;
    }

    //
    // ensure Password and TrustedDomainName are the correct length
    //
    if(lstrlenW(Password) > LM20_PWLEN)
        Password[LM20_PWLEN] = L'\0'; // truncate

    if(lstrlenW(TrustedDomainName) > MAX_COMPUTERNAME_LENGTH)
        TrustedDomainName[MAX_COMPUTERNAME_LENGTH] = L'\0'; // truncate

    //
    // obtain the primary DC computer name from the specified
    // TargetDomainName
    //
    nas = NetGetDCName(
        NULL, TargetDomainName, (LPBYTE *)&DomainController);

    if(nas != NERR_Success) {
        DisplayError("NetGetDCName", nas);
        return RTN_ERROR;
    }

    //
    // verify the trust relationship
    //
    if(!VerifyTrustRelationship(
        TrustedDomainName,
        TargetDomainName,   // trust account to verify
        Password,
        &bTrustVerified
        )) {
        //
        // an error occurred during trust relationship verification
        //
        DisplayError("VerifyTrustRelationship", GetLastError());
    }
    else {
        //
        // inform the user if the trust was not verified
        //
        if(!bTrustVerified) {
            DWORD dwTrustVerifyFailReason = GetLastError();

            //
            // You could exit here, but this is optional. Reasons for
            // trust verification failure could be non-existent
            // "permitted to trust" on the verified domain (rc=1317),
            // wrong password (rc=1326), etc
            //
            printf("Warning:  ");

            switch(dwTrustVerifyFailReason) {
                case ERROR_NO_SUCH_USER:
                    printf("%ls not permitted-to-trust on %ls\n",
                        TargetDomainName, TrustedDomainName);
                    break;

                case ERROR_LOGON_FAILURE:
                    printf("Trust %ls has incorrect password on %ls\n",
                        TrustedDomainName, TargetDomainName);
                    break;

                default:
                    DisplayError("Trust was not verified.  Non-fatal",
                        dwTrustVerifyFailReason);
            } // switch
        }
    }

    //
    // fetch the DomainSid of the domain to trust
    //
    if(!GetDomainSid(TrustedDomainName, &DomainSid)) {
        DisplayError("GetDomainSid", GetLastError());
        return RTN_ERROR;
    }

    //
    // open the policy on the target domain
    //
    Status = OpenPolicy(
                DomainController,
                POLICY_CREATE_SECRET |  // for password set operation
                POLICY_TRUST_ADMIN,     // for trust creation
                &PolicyHandle
                );

    if(Status != STATUS_SUCCESS) {
        DisplayNtStatus("OpenPolicy", Status);
        return RTN_ERROR;
    }

    //
    // Update TrustedDomainInfo to reflect the specified trust.
    //
    Status = SetTrustedDomainInfo(
        PolicyHandle,
        DomainSid,
        TrustedDomainName,
        Password
        );

    if(Status != STATUS_SUCCESS) {
        DisplayNtStatus("SetTrustedDomainInfo", Status);
        return RTN_ERROR;
    }

    //
    // if you allocated memory for TargetDomainName, free it
    //
    if(bAllocTargetDomain)
        HeapFree(GetProcessHeap(), 0, TargetDomainName);

    //
    // free the Sid which was allocated for the TrustedDomain Sid
    //
    FreeSid(DomainSid);

    //
    // close the policy handle
    //
    LsaClose(PolicyHandle);

    if(DomainController != NULL)
        NetApiBufferFree(DomainController);

    return RTN_OK;
}

void
InitLsaString(
    PLSA_UNICODE_STRING LsaString,
    LPWSTR String
    )
{
    DWORD StringLength;

    if(String == NULL) {
        LsaString->Buffer = NULL;
        LsaString->Length = 0;
        LsaString->MaximumLength = 0;

        return;
    }

    StringLength = lstrlenW(String);
    LsaString->Buffer = String;
    LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
    LsaString->MaximumLength = (USHORT) (StringLength + 1) *
        sizeof(WCHAR);
}

NTSTATUS
OpenPolicy(
    LPWSTR ServerName,
    DWORD DesiredAccess,
    PLSA_HANDLE PolicyHandle
    )
{
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    LSA_UNICODE_STRING ServerString;
    PLSA_UNICODE_STRING Server;

    //
    // Always initialize the object attributes to all zeroes
    //
    ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

    if(ServerName != NULL) {
        //
        // Make a LSA_UNICODE_STRING out of the LPWSTR passed in
        //
        InitLsaString(&ServerString, ServerName);

        Server = &ServerString;
    } else {
        Server = NULL;
    }

    //
    // Attempt to open the policy
    //
    return LsaOpenPolicy(
                Server,
                &ObjectAttributes,
                DesiredAccess,
                PolicyHandle
                );
}

/*++
 This function retrieves the Sid representing the specified DomainName.

 If the function succeeds, the return value is TRUE and pDomainSid will
 point to the Sid representing the specified DomainName. The caller
 should free the memory associated with this Sid with the Win32 API
 FreeSid() when the Sid is no longer needed.

 If the function fails, the return value is FALSE, and pDomainSid will
 be set to NULL.

--*/

BOOL
GetDomainSid(
    LPWSTR DomainName,  // domain name to acquire Sid of
    PSID *pDomainSid    // points to allocated Sid on success
    )
{
    NET_API_STATUS nas;
    LPWSTR DomainController;
    PUSER_MODALS_INFO_2 umi2 = NULL;
    DWORD dwSidSize;
    BOOL bSuccess = FALSE; // assume this function will fail

    *pDomainSid = NULL; // invalidate pointer

    //
    // obtain the PDC computer name of the supplied domain name
    //
    nas = NetGetDCName(NULL, DomainName, (LPBYTE *)&DomainController);
    if(nas != NERR_Success) {
        SetLastError(nas);
        return FALSE;
    }

    __try {

    //
    // obtain the domain Sid from the PDC
    // NOTE: this may fail if the credentials of the caller are not
    // recognized or valid on the target computer. In this case, it
    // is necessary to first establish a connection via NetUseAdd()
    // at info-level 2 to the remote IPC$, specifying EMPTY
    // credentials.
    //
    nas = NetUserModalsGet(DomainController, 2, (LPBYTE *)&umi2);
    if(nas != NERR_Success) __leave;

    //
    // if the Sid is valid, obtain the size of the Sid
    //
    if(!IsValidSid(umi2->usrmod2_domain_id)) __leave;
    dwSidSize = GetLengthSid(umi2->usrmod2_domain_id);

    //
    // allocate storage and copy the Sid
    //
    *pDomainSid = (PSID)HeapAlloc(GetProcessHeap(), 0, dwSidSize);
    if(*pDomainSid == NULL) __leave;

    if(!CopySid(dwSidSize, *pDomainSid, umi2->usrmod2_domain_id))
        __leave;

    bSuccess = TRUE; // indicate success

    } // try
    __finally {

    //
    // free allocated memory
    //
    NetApiBufferFree(DomainController);

    if(umi2 != NULL)
        NetApiBufferFree(umi2);

    if(!bSuccess) {
        //
        // if the function failed, free memory and indicate result code
        //

        if(*pDomainSid != NULL) {
            FreeSid(*pDomainSid);
            *pDomainSid = NULL;
        }

        if(nas != NERR_Success) {
            SetLastError(nas);
        }
    }

    }
    return bSuccess;
}

/*++
 This function manipulates the trust associated with the supplied
 DomainSid.

 If the domain trust does not exist, it is created with the
 specified password. In this case, the supplied PolicyHandle must
 have been opened with POLICY_TRUST_ADMIN and POLICY_CREATE_SECRET
 access to the policy object.

 If the domain trust exists, the password is updated with the
 password specified by the Password parameter. The trust OldPassword
 is set to the previous password.

 If DomainName and Password are NULL, the trusted domain specified by
 DomainSid is deleted.

--*/

NTSTATUS
SetTrustedDomainInfo(
    LSA_HANDLE PolicyHandle,
    PSID DomainSid,             // Sid of domain to manipulate
    LPWSTR TrustedDomainName,   // trusted domain name to add/update
    LPWSTR Password             // new trust password for trusted domain
    )
{
    PTRUSTED_PASSWORD_INFO tpi;
//    PTRUSTED_DOMAIN_NAME_INFO tdni;
    LSA_UNICODE_STRING LsaPassword;
    TRUSTED_PASSWORD_INFO Newtpi;
    BOOL bTrustExists = TRUE; // assume trust exists
    NTSTATUS Status;

    //
    // Make sure you were passed a valid Sid
    //
    if(DomainSid == NULL || !IsValidSid(DomainSid))
        return STATUS_INVALID_SID;

    //
    // if TristedDomainName and Password is NULL (or emptry strings),
    // delete the specified trust
    //
    if( (TrustedDomainName == NULL || *TrustedDomainName == L'\0') &&
        (Password == NULL || *Password == L'\0') ) {
        return LsaDeleteTrustedDomain(PolicyHandle, DomainSid);
    }

    //
    // query the current password, which is used to update the
    // OldPassword. If the trust doesn't exist, indicate this so that the
    // trust will be created.
    //
    Status = LsaQueryTrustedDomainInfo(
        PolicyHandle,
        DomainSid,
        TrustedPasswordInformation,
        &tpi);

    if(Status != STATUS_SUCCESS) {
        if(Status == STATUS_OBJECT_NAME_NOT_FOUND) {
            bTrustExists = FALSE; // indicate trust does not exist
        }
        else {
            DisplayNtStatus("LsaQueryTrustedDomainInfo", Status);
            return Status;
        }
    }

    InitLsaString(&LsaPassword, Password);

    //
    // set the new password to the supplied password
    //
    Newtpi.Password = LsaPassword;

    if(bTrustExists) {
        //
        // if the trust already existed, set OldPassword to the
        // current password...
        //
        Newtpi.OldPassword = tpi->Password;
    }
    else {
        LSA_UNICODE_STRING LsaDomainName;
        DWORD cchDomainName; // number of chars in TrustedDomainName

        //
        // if the trust did not exist, set OldPassword to the
        // supplied password...
        //
        Newtpi.OldPassword = LsaPassword;

        InitLsaString(&LsaDomainName, TrustedDomainName);

        //
        // ...convert TrustedDomainName to uppercase...
        //
        cchDomainName = LsaDomainName.Length / sizeof(WCHAR);
        while(cchDomainName--) {
            LsaDomainName.Buffer[cchDomainName] =
             towupper(LsaDomainName.Buffer[cchDomainName]);
        }

        //
        // ...create the trusted domain object
        //
        Status = LsaSetTrustedDomainInformation(
            PolicyHandle,
            DomainSid,
            TrustedDomainNameInformation,
            &LsaDomainName
            );

        if(Status != STATUS_SUCCESS) return Status;
    }

    //
    // update TrustedPasswordInformation for the specified trust
    //
    Status = LsaSetTrustedDomainInformation(
        PolicyHandle,
        DomainSid,
        TrustedPasswordInformation,
        &Newtpi
        );

    //
    // if a trust already existed, free the memory associated with
    // the retrieved information
    //
    if(bTrustExists) LsaFreeMemory(tpi);

    return Status;
}

/*++

 This function verifies that the "permitted to trust" side of the
 trust relationship has been established and that the specified
 password matches the "permitted to trust" password.

 If the function fails, the return value is FALSE, and bTrustVerified
 is undefined.

 If the function succeeds, the trust verification succeeded, and
 bTrustVerified is set to the result of the verification.

 If bTrustVerified is TRUE, the trust relationship is verfied.

 If bTrustVerified is FALSE, the trust was not verified.  Call
 GetLastError() to get extended error information.

 ERROR_NO_SUCH_USER indicates that the "permitted to trust"
 UF_INTERDOMAIN_TRUST_ACCOUNT does not exist on the specified domain.

 ERROR_LOGON_FAILURE indicates that the supplied password does
 not match the password in the "permitted to trust"
 UF_INTERDOMAIN_TRUST_ACCOUNT.

 NOTE: if a connection exists to the domain controller of the
 TargetDomainName prior to calling this function, this function will
 fail due to a credential conflict (WinError == 1219).

--*/

BOOL
VerifyTrustRelationship(
    LPWSTR TargetDomainName,        // domain name to verify trust at
    LPWSTR TrustAccountToVerify,    // trusted domain name to verify
    LPWSTR Password,                // password associated with trust
    LPBOOL bTrustVerified           // indicates if trust was verified
    )
{
    NET_API_STATUS nas;
    USE_INFO_2 ui2;
    LPWSTR DomainController;
    LPWSTR szIpc = L"\\IPC$";
    LPWSTR RemoteResource = NULL;
    LPWSTR UserName = NULL;
    BOOL bSuccess = FALSE; // assume this function will fail

    //
    // fetch the computer name of the domain you try to connect to
    //
    nas = NetGetDCName(
        NULL, TargetDomainName, (LPBYTE *)&DomainController);
    if(nas != NERR_Success) {
        SetLastError(nas);
        return FALSE;
    }

    __try {

    //
    // build the \\<DCName>\ipc$ as the remote resource
    //
    RemoteResource = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
        (lstrlenW(DomainController) + lstrlenW(szIpc) + 1 ) *
            sizeof(WCHAR)
        );

    if(RemoteResource == NULL) __leave;

    if(lstrcpyW(RemoteResource, DomainController) == NULL) __leave;
    if(lstrcatW(RemoteResource, szIpc) == NULL) __leave;

    //
    // build the user name as <TrustAccountToVerify>$
    //
    UserName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
        (lstrlenW(TrustAccountToVerify) + 1 + 1) * sizeof(WCHAR)
        );

    if(UserName == NULL) __leave;

    if(lstrcpyW(UserName, TrustAccountToVerify) == NULL) __leave;
    if(lstrcatW(UserName, L"$") == NULL) __leave;

    ZeroMemory(&ui2, sizeof(ui2));

    ui2.ui2_local = NULL;
    ui2.ui2_remote = RemoteResource;
    ui2.ui2_asg_type = USE_IPC;
    ui2.ui2_password = Password;
    ui2.ui2_username = UserName;
    ui2.ui2_domainname = TargetDomainName;

    //
    // Attempt to establish a connection to the target domain.
    // If the result is ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT,
    // this illustrates the supplied Password matches an existing
    // trust account because the credentials were validated but
    // a logon is not allowed for this account type.
    //
    nas = NetUseAdd(NULL, 2, (LPBYTE)&ui2, NULL);

    if(nas == ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT) {
        *bTrustVerified = TRUE;     // indicate trust verified
    }
    else {
        *bTrustVerified = FALSE;    // indicate trust not verified

        //
        // if the connection succeeded, the UF_INTERDOMAIN_TRUST_ACCOUNT
        // does not exist, because the supplied credentials aren't
        // recognized by the remote (default credentials are applied).
        // Delete the connection and indicate the "permitted to trust"
        // account is non-existent.
        //
        if(nas == NERR_Success) {
            NetUseDel(NULL, RemoteResource, 0);
            nas = ERROR_NO_SUCH_USER;
        }
    }

    bSuccess = TRUE; // indicate this function succeeded

    } // try
    __finally {

    //
    // free allocated memory
    //
    NetApiBufferFree(DomainController);

    if(RemoteResource != NULL) {
        HeapFree(GetProcessHeap(), 0, RemoteResource);
    }

    if(UserName != NULL) {
        HeapFree(GetProcessHeap(), 0, UserName);
    }

    //
    // if you succeeded, but trust could not be verified, indicate why
    //
    if(bSuccess == TRUE && *bTrustVerified == FALSE) {
        SetLastError(nas);
    }

    }

    return bSuccess;
}

void
DisplayNtStatus(
    LPSTR szAPI,    // ANSI string containing API name
    NTSTATUS Status
    )
{
    //
    // convert the NTSTATUS to Winerror and display the result
    //
    DisplayError(szAPI, LsaNtStatusToWinError(Status));
}

void
DisplayError(
    LPSTR szAPI,    // pointer to failed API name
    DWORD dwLastError
    )
{
    HMODULE hModule = NULL;
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    fprintf(stderr,"%s error! (rc=%lu)\n", szAPI, dwLastError);

    if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
        hModule = LoadLibraryEx(
            TEXT("netmsg.dll"),
            NULL,
            LOAD_LIBRARY_AS_DATAFILE
            );
    }

    if(dwBufferLength=FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_IGNORE_INSERTS |
        FORMAT_MESSAGE_FROM_SYSTEM |
        ((hModule != NULL) ? FORMAT_MESSAGE_FROM_HMODULE : 0),
        hModule, // module to get message from
        dwLastError,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
        (LPSTR) &MessageBuffer,
        0,
        NULL
        ))
    {
        DWORD dwBytesWritten;

        //
        // Output message string on stderr
        //
        WriteFile(
            GetStdHandle(STD_ERROR_HANDLE),
            MessageBuffer,
            dwBufferLength,
            &dwBytesWritten,
            NULL
            );

        //
        // free the buffer allocated by the system
        //
        LocalFree(MessageBuffer);
    }

    if(hModule != NULL)
        FreeLibrary(hModule);
}


README.TXT

Graphical Identification and Authentication DLL (GINA)

The GINA sample demonstrates the Gina-related functions.

To install the Gina DLL, copy it into %systemroot%\SYSTEM32, add
a registry value of type REG_SZ named GinaDLL under the key
\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon,
and reboot the machine.

WARNING! - It is possible to make your machine unusable if you install a
replacement Gina and it fails. To avoid this situation, be sure that you
have a method of accessing the %systemroot%\SYSTEM32 directory independent
of the Windows NT installation you are testing on.

If the Gina sample malfunctions, and you are not able to log on to fix
the problem, you can recover by doing one of the following:

* If the test machine is on the network and you have an account on
  another machine with Administrative privileges on the test machine,
  open the test machine registry remotely with regedt32 and delete
  the GinaDLL value.

* If the test machine is on the network, and %systemroot%\SYSTEM32 is
  available on a network share for that machine (e.g. \\<name>\c$),
  rename GINA.DLL to something else with a command like

     ren \\<name>\c$\winnt351\system32\gina.dll gina.sdk

  reboot the test machine, and delete the GinaDLL value from the registry.

* If the test machine will dual boot to another version of Windows NT
  or another operating system capable of accessing %systemroot%\system32,
  boot to that operating system, delete %systemroot%\system32\GINA.DLL,
  reboot, and delete the GinaDLL value from the registry.

For more information see GINA.HLP.


DEBUG.H

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       debug.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    8-02-94   RichardW   Created
//
//----------------------------------------------------------------------------

#ifndef __DEBUG_H__
#define __DEBUG_H__

#if DBG

extern  DWORD   GINAInfoLevel;

#define DebugLog(x) LogEvent x

void    LogEvent(long, const char *, ...);
void    InitDebugSupport(void);

#define DEB_ERROR           0x00000001
#define DEB_WARN            0x00000002
#define DEB_TRACE           0x00000004

#else

#define DebugLog(x)

#endif

#endif // __DEBUG_H__


GINA.C

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       gina.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-20-95   RichardW   Created
//
//----------------------------------------------------------------------------

#include "gina.h"
#pragma hdrstop

HINSTANCE                   hDllInstance;   // My instance, for resource loading
HANDLE                      hGlobalWlx;     // Handle to tell winlogon who's calling
PWLX_DISPATCH_VERSION_1_0   pWlxFuncs;      // Ptr to table of functions

#define WINLOGON_APP        TEXT("Winlogon")
#define USERINIT            TEXT("Userinit")
#define USERINIT_DEFAULT    TEXT("Userinit.exe")

//+---------------------------------------------------------------------------
//
//  Function:   DllMain
//
//  Synopsis:   DLL Entrance point
//
//  Arguments:  [hInstance]  --
//              [dwReason]   --
//              [lpReserved] --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
WINAPI
DllMain(
    HINSTANCE       hInstance,
    DWORD           dwReason,
    LPVOID          lpReserved)
{
    switch (dwReason)
    {
        case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls ( hInstance );
            hDllInstance = hInstance;
#if DBG
            InitDebugSupport();
#endif
        case DLL_PROCESS_DETACH:
        default:
            return(TRUE);
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   WlxNegotiate
//
//  Synopsis:   Negotiate version of interface with Winlogon
//
//  Arguments:  [dwWinlogonVersion] --
//              [pdwDllVersion]     --
//
//  Algorithm:
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
WINAPI
WlxNegotiate(
    DWORD                   dwWinlogonVersion,
    DWORD                   *pdwDllVersion
    )
{
    if (dwWinlogonVersion < WLX_CURRENT_VERSION)
    {
        DebugLog((DEB_ERROR, "Unknown version: %d\n", dwWinlogonVersion));
        return(FALSE);
    }

    *pdwDllVersion = WLX_CURRENT_VERSION;

    DebugLog((DEB_TRACE, "Negotiate:  successful!\n"));

    return(TRUE);

}

//+---------------------------------------------------------------------------
//
//  Function:   WlxInitialize
//
//  Synopsis:   Initialize entrypoint from winlogon
//
//  Arguments:  [lpWinsta]           --
//              [hWlx]               --
//              [pvReserved]         --
//              [pWinlogonFunctions] --
//              [pWlxContext]        --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
WINAPI
WlxInitialize(
    LPWSTR                  lpWinsta,
    HANDLE                  hWlx,
    PVOID                   pvReserved,
    PVOID                   pWinlogonFunctions,
    PVOID                   *pWlxContext
    )
{
    PGlobals  pGlobals;

    pWlxFuncs = (PWLX_DISPATCH_VERSION_1_0) pWinlogonFunctions;

    hGlobalWlx = hWlx;

    pGlobals = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(Globals));

    *pWlxContext = (PVOID) pGlobals;

    //
    // Read from registry...
    //

    pGlobals->fAllowNewUser = TRUE;

    pWlxFuncs->WlxUseCtrlAltDel(hWlx);

    InitCommonControls();

    return(TRUE);
}

//+---------------------------------------------------------------------------
//
//  Function:   WlxDisplaySASNotice
//
//  Synopsis:   Where we display the welcome, we're waiting dialog box
//
//  Arguments:  [pContext] --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
WINAPI
WlxDisplaySASNotice(PVOID   pContext)
{
    int Result;

    Result = pWlxFuncs->WlxDialogBoxParam(  hGlobalWlx,
                                            hDllInstance,
                                            (LPTSTR) MAKEINTRESOURCE(IDD_WELCOME_DLG),
                                            NULL,
                                            WelcomeDlgProc,
                                            0 );
}

//+---------------------------------------------------------------------------
//
//  Function:   WlxLoggedOutSAS
//
//  Synopsis:   Called when no one logged on...
//
//  Arguments:  [pWlxContext]       --
//              [dwSasType]         --
//              [pAuthenticationId] --
//              [pLogonSid]         --
//              [pdwOptions]        --
//              [phToken]           --
//              [pMprNotifyInfo]    --
//              [pProfile]          --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
int
WINAPI
WlxLoggedOutSAS(
    PVOID                   pWlxContext,
    DWORD                   dwSasType,
    PLUID                   pAuthenticationId,
    PSID                    pLogonSid,
    PDWORD                  pdwOptions,
    PHANDLE                 phToken,
    PWLX_MPR_NOTIFY_INFO    pMprNotifyInfo,
    PVOID *                 pProfile
    )
{
    int         result;
    // PWLX_PROFILE_V1_0   pWlxProfile;
    // PMiniAccount    pAccount;
    PGlobals        pGlobals;

    pGlobals = (PGlobals) pWlxContext;

    result = pWlxFuncs->WlxDialogBoxParam(  hGlobalWlx,
                                            hDllInstance,
                                            (LPTSTR) MAKEINTRESOURCE(IDD_LOGON_DIALOG),
                                            NULL,
                                            LogonDlgProc,
                                            (LPARAM) pGlobals );

    if (result == WLX_SAS_ACTION_LOGON)
    {
        result = AttemptLogon(pGlobals, pGlobals->pAccount,
                                pLogonSid, pAuthenticationId);

        if (result == WLX_SAS_ACTION_LOGON)
        {
            *pdwOptions = 0;
            *phToken = pGlobals->hUserToken;
            *pProfile = NULL;

            pMprNotifyInfo->pszUserName = DupString(pGlobals->pAccount->pszUsername);
            pMprNotifyInfo->pszDomain = DupString(pGlobals->pAccount->pszDomain);
            pMprNotifyInfo->pszPassword = DupString(pGlobals->pAccount->pszPassword);
            pMprNotifyInfo->pszOldPassword = NULL;

        }
    }
    return(result);
}

//+---------------------------------------------------------------------------
//
//  Function:   WlxActivateUserShell
//
//  Synopsis:   Activates progman or whatever for the user
//
//  Arguments:  [pWlxContext]       --
//              [pszDesktop]        --
//              [pszMprLogonScript] --
//              [pEnvironment]      --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
WINAPI
WlxActivateUserShell(
    PVOID                   pWlxContext,
    PWSTR                   pszDesktop,
    PWSTR                   pszMprLogonScript,
    PVOID                   pEnvironment
    )
{
    // BOOL        bExec;
    WCHAR       szText[MAX_PATH];
    PWSTR       pszScan;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    PGlobals    pGlobals;
    DWORD       StartCount;

    pGlobals = (PGlobals) pWlxContext;

    GetProfileString(WINLOGON_APP, USERINIT, USERINIT_DEFAULT, szText, MAX_PATH);

    StartCount = 0;

    pszScan = wcstok(szText, TEXT(","));
    while (pszScan)
    {
        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(STARTUPINFO);
        si.lpTitle = pszScan;
        si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L;
        si.dwFlags = 0;
        si.wShowWindow = SW_SHOW;   // at least let the guy see it
        si.lpReserved2 = NULL;
        si.cbReserved2 = 0;
        si.lpDesktop = pszDesktop;

        DebugLog((DEB_TRACE, "Starting '%ws' as user\n", pszScan));

        ImpersonateLoggedOnUser(pGlobals->hUserToken);

        if (CreateProcessAsUser(pGlobals->hUserToken,   // Token to run as
                            NULL,                   // App name
                            pszScan,                // Command Line
                            NULL,                   // Process SD
                            NULL,                   // Thread SD
                            FALSE,                  // No inherit
                            CREATE_UNICODE_ENVIRONMENT,
                            pEnvironment,
                            NULL,
                            &si,
                            &pi))
        {
            StartCount++;
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
        }

        RevertToSelf();

        pszScan = wcstok(NULL, TEXT(","));
    }

    return(StartCount > 0);
}

//+---------------------------------------------------------------------------
//
//  Function:   WlxLoggedOnSAS
//
//  Synopsis:   Called when someone hits CAD when we're logged on
//
//  Arguments:  [pWlxContext] --
//              [dwSasType]   --
//              [pReserved]   --
//
//  Algorithm:
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
int
WINAPI
WlxLoggedOnSAS(
    PVOID                   pWlxContext,
    DWORD                   dwSasType,
    PVOID                   pReserved
    )
{
    int result;

    result = pWlxFuncs->WlxDialogBoxParam(  hGlobalWlx,
                                            hDllInstance,
                                            (LPTSTR) MAKEINTRESOURCE(IDD_OPTIONS_DIALOG),
                                            NULL,
                                            OptionsDlgProc,
                                            (LPARAM) pWlxContext );

    return(result);

}

//+---------------------------------------------------------------------------
//
//  Function:   WlxIsLockOk
//
//  Synopsis:   Called to make sure that locking is ok
//
//  Arguments:  [pWlxContext] --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
WINAPI
WlxIsLockOk(
    PVOID                   pWlxContext
    )
{
    return(TRUE);
}

//+---------------------------------------------------------------------------
//
//  Function:   WlxDisplayLockedNotice
//
//  Synopsis:   Displays a notice while the workstation is locked
//
//  Arguments:  [pWlxContext] --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
WINAPI
WlxDisplayLockedNotice(PVOID   pWlxContext)
{
    int Result;

    Result = pWlxFuncs->WlxDialogBoxParam(  hGlobalWlx,
                                            hDllInstance,
                                            (LPTSTR) MAKEINTRESOURCE(IDD_WKSTA_LOCKED),
                                            NULL,
                                            WelcomeDlgProc,
                                            0 );

    return;
}

//+---------------------------------------------------------------------------
//
//  Function:   WlxWkstaLockedSAS
//
//  Synopsis:   Responds during an unlock attempt
//
//  Arguments:  [pWlxContext] --
//              [dwSasType]   --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
int
WINAPI
WlxWkstaLockedSAS(
    PVOID                   pWlxContext,
    DWORD                   dwSasType
    )
{
    return(WLX_SAS_ACTION_UNLOCK_WKSTA);
}

//+---------------------------------------------------------------------------
//
//  Function:   WlxIsLogoffOk
//
//  Synopsis:   Called to make sure that logoff is ok
//
//  Arguments:  [pWlxContext] --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
WINAPI
WlxIsLogoffOk(
    PVOID                   pWlxContext
    )
{
    return(TRUE);
}

//+---------------------------------------------------------------------------
//
//  Function:   WlxLogoff
//
//  Synopsis:   Called when the user logs off
//
//  Arguments:  [pWlxContext] --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
WINAPI
WlxLogoff(
    PVOID                   pWlxContext
    )
{
    PGlobals    pGlobals;

    pGlobals = (PGlobals) pWlxContext;

    //
    // Winlogon has closed it for us..
    //

    pGlobals->hUserToken = NULL;
    pGlobals->pAccount = NULL;

    return;
}

//+---------------------------------------------------------------------------
//
//  Function:   WlxShutdown
//
//  Synopsis:   Called before shutdown so that we can unload/clean up.
//
//  Arguments:  [pWlxContext]  --
//              [ShutdownType] --
//
//  History:    4-20-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
WINAPI
WlxShutdown(
    PVOID                   pWlxContext,
    DWORD                   ShutdownType
    )
{
    return;
}


GINA.H

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       gina.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-20-95   RichardW   Created
//
//----------------------------------------------------------------------------

//
// Make sure that you have defined UNICODE, _UNICODE, and WIN32 correctly in
// your build environment.
//

#include <windows.h>
#include <commctrl.h>
#include <lm.h>
#include <winwlx.h>

#include "ginadlg.h"
#include "resids.h"

#include "debug.h"
#include "structs.h"
#include "protos.h"
#include "globals.h"


GINADLG.H

#define IDD_WELCOME_DLG             100
#define IDD_WELCOME_TEXT            101
#define IDD_LOGON_LV                201
#define IDD_SHUTDOWN_BUTTON         202
#define IDD_LOGON_BUTTON            203
#define IDD_LOGON_DIALOG            300
#define IDD_OPTIONS_DIALOG          400
#define IDD_OPTIONS_ICON_SPOT       401
#define IDD_OPTIONS_ICON            402
#define IDD_OPTIONS_LOGON_TEXT      403
#define IDD_LOCK_BUTTON             405
#define IDD_PASSWORD_BUTTON         406
#define IDD_TASK_BUTTON             407
#define IDD_HELP_BUTTON             408
#define IDD_OPTION_TEXT             409
#define IDD_OPTIONS_EXIT            410
#define IDD_SHUTDOWN                500
#define IDD_CONFIRM_TEXT            501
#define IDD_CONFIRM_LOGOFF          502
#define IDD_CONFIRM_SHUTDOWN        503
#define IDD_CONFIRM_REBOOT          504
#define IDD_SHUTDOWN_ICON_SPOT      506
#define IDD_SHUTDOWN_ICON           507
#define IDD_CONFIG_BUTTON           411
#define IDD_NEW_USER_LOGON          600
#define IDD_NEW_USER_ICON_SPOT      601
#define IDD_NEW_USER_ICON_X         602
#define IDD_USERNAME_TEXT           603
#define IDD_USER_NAME               604
#define IDD_DOMAIN_TEXT             605
#define IDD_DOMAIN                  606
#define IDD_PASSWORD_TEXT           607
#define IDD_PASSWORD                608
#define IDD_LOGON_CONFIG            700
#define IDD_AUTO_LOGON              701
#define IDD_AUTO_LOGOFF_LOGON       702
#define IDD_LOCK_OTHERS             703
#define IDD_CONFIRM_POWEROFF        508
#define IDD_CHANGE_PASSWORD         800
#define IDD_USER_NAME_TEXT          801
#define IDD_NEW_PASSWORD_TEXT       802
#define IDD_FROM_TEXT               803
#define IDD_OLD_PASSWORD_TEXT       804
#define IDD_OLD_PASSWORD            805
#define IDD_NEW_PASSWORD            807
#define IDD_CONFIRM_PASSWORD_TEXT   808
#define IDD_CONFIRM_PASSWORD        809
#define IDD_FROM_TEXT               803
#define IDD_NO_NEW_USERS            704
#define IDD_WKSTA_LOCKED            200
#define IDD_LOCKED_ICON_SPOT        204
#define IDI_LOCKED_ICON             205
#define IDD_LOCKED_TEXT             206
#define IDD_LOCKED_BY_LABEL         207
#define IDD_LOCKED_BY               208
#define IDD_LOCKED_INST             209


GLOBALS.H

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       globals.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-20-95   RichardW   Created
//
//----------------------------------------------------------------------------

extern  HINSTANCE                   hDllInstance;
extern  HANDLE                      hGlobalWlx;
extern  PWLX_DISPATCH_VERSION_1_0   pWlxFuncs;

#define GINA_SAS_1      256


LOGON.C

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       logon.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-28-95   RichardW   Created
//
//----------------------------------------------------------------------------

#include "gina.h"
#pragma hdrstop

HIMAGELIST      hiLogonSmall;
HIMAGELIST      hiLogonLarge;
PMiniAccount    pAccountList;
WCHAR           szMiniKey[] = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Accounts");

BYTE            LongPseudoRandomString[] = {0x27, 0xbd, 0xff, 0xa0,
                                            0xaf, 0xbf, 0x00, 0x1c,
                                            0x24, 0x0e, 0x00, 0x01,
                                            0x24, 0x0f, 0x00, 0x05 };

MiniAccount TestAccounts[]  = { {NULL, TEXT("daveth"), TEXT("\\msft\\risc\\dev"), TEXT("daveth"), TEXT("Oooh"), 0, MINI_CAN_EDIT},
                                {NULL, TEXT("Test1"), TEXT("Redmond"), TEXT("Test1"), TEXT("Mine"), 0, MINI_CAN_EDIT},
                                {NULL, TEXT("Test2"), TEXT("NtWksta"), TEXT("Test2"), TEXT("Yours"), 0, 0},
                                {NULL, TEXT("New User"), TEXT(""), TEXT(""), TEXT(""), 0, MINI_NEW_ACCOUNT}

                              };

BOOL
SaveMiniAccount(PMiniAccount    pAccount)
{
    HKEY                    hMiniKey;
    PSerializedMiniAccount  pPacked;
    DWORD                   cbNeeded;
    PWSTR                   pszPack;
    int                     err;
    DWORD                   Disposition;

    err = RegCreateKeyEx(   HKEY_LOCAL_MACHINE,
                            szMiniKey,
                            0,
                            TEXT(""),
                            REG_OPTION_NON_VOLATILE,
                            KEY_WRITE | KEY_READ,
                            NULL,
                            &hMiniKey,
                            &Disposition);

    if (err)
    {
        return(FALSE);
    }

    cbNeeded = sizeof(SerializedMiniAccount) +
                (wcslen(pAccount->pszDomain) + 1 +
                 wcslen(pAccount->pszPassword) + 1 +
                 wcslen(pAccount->pszComment) + 1 ) * sizeof(WCHAR) ;

    pPacked = LocalAlloc(LMEM_FIXED, cbNeeded);

    if (!pPacked)
    {
        return(FALSE);
    }

    pszPack = (PWSTR) (pPacked + 1);

    pPacked->Version = MINI_VERSION;
    pPacked->Flags = pAccount->Flags;
    pPacked->IconId = pAccount->IconId;

    pPacked->dwDomainOffset = sizeof(SerializedMiniAccount);
    pPacked->dwDomainLength = (wcslen(pAccount->pszDomain) + 1) * sizeof(WCHAR);
    wcscpy(pszPack, pAccount->pszDomain);
    pszPack += (pPacked->dwDomainLength / sizeof(WCHAR) );

    pPacked->dwPasswordOffset = pPacked->dwDomainOffset + pPacked->dwDomainLength;
    pPacked->dwPasswordLength = (wcslen(pAccount->pszPassword) + 1) * sizeof(WCHAR);
    wcscpy(pszPack, pAccount->pszPassword);
    pszPack += (pPacked->dwPasswordLength / sizeof(WCHAR) );

    pPacked->dwCommentOffset = pPacked->dwPasswordOffset + pPacked->dwPasswordLength;
    pPacked->dwCommentLength = (wcslen(pAccount->pszComment) + 1) * sizeof(WCHAR);
    wcscpy(pszPack, pAccount->pszComment);

    err = RegSetValueEx(hMiniKey,
                        pAccount->pszUsername,
                        0,
                        REG_BINARY,
                        (PBYTE) pPacked,
                        cbNeeded);

    RegCloseKey(hMiniKey);

    return(err == 0);
}

BOOL
LoadMiniAccounts(PGlobals   pGlobals)
{
    FILETIME            LastWrite;
    // HKEY                hKey;
    HKEY                hMiniKey;
    WCHAR               szClass[64];
    DWORD               err;
    DWORD               Disposition;
    DWORD               Class;
    DWORD               cKeys;
    DWORD               LongestKeyName;
    DWORD               LongestClass;
    DWORD               cValues;
    DWORD               LongestValueName;
    DWORD               LongestValueData;
    DWORD               Security;
    DWORD               i;
    WCHAR               szValue[MAX_PATH];
    DWORD               cbValue;
    DWORD               dwType;
    DWORD               cbData;
    PBYTE               pBuffer;
    DWORD               cbBuffer;
    PMiniAccount        pAccount;
    PSerializedMiniAccount  pPacked;

    if (pGlobals->fAllowNewUser)
    {
        pAccount = LocalAlloc(LMEM_FIXED, sizeof(MiniAccount) );
        if (pAccount)
        {
            pAccount->pNext = NULL;
            pAccount->pszUsername = TEXT("New User");
            pAccount->pszDomain = TEXT("");
            pAccount->pszPassword = TEXT("");
            pAccount->pszComment = TEXT("");
            pAccount->Flags = MINI_NEW_ACCOUNT;

            pAccountList = pAccount;
        }
        else
            return(FALSE);
    }
    else
    {
        pAccountList = NULL;
    }

    //
    //

    err = RegCreateKeyEx(   HKEY_LOCAL_MACHINE,
                            szMiniKey,
                            0,
                            TEXT(""),
                            REG_OPTION_NON_VOLATILE,
                            KEY_WRITE | KEY_READ,
                            NULL,
                            &hMiniKey,
                            &Disposition);

    if (err)
    {
        return(FALSE);
    }

    if (Disposition == REG_OPENED_EXISTING_KEY)
    {
        //
        // Enumerate the sub keys of our class, and Load them.
        //
        Class = sizeof(szClass) / sizeof(WCHAR);
        err = RegQueryInfoKey(  hMiniKey,
                                szClass,
                                &Class,
                                NULL,
                                &cKeys,
                                &LongestKeyName,
                                &LongestClass,
                                &cValues,
                                &LongestValueName,
                                &LongestValueData,
                                &Security,
                                &LastWrite);

        pBuffer = LocalAlloc(LMEM_FIXED, 512);
        cbBuffer = 512;

        for (i = 0; i < cValues ; i++ )
        {
            cbValue = MAX_PATH;

            err = RegEnumValue( hMiniKey,
                                i,
                                szValue,
                                &cbValue,
                                NULL,
                                &dwType,
                                NULL,
                                &cbData);

            if (err)
            {
                break;
            }

            if (dwType != REG_BINARY)
            {
                continue;
            }

            if (cbData > cbBuffer)
            {
                pBuffer = LocalReAlloc(pBuffer, LMEM_FIXED, cbData);
                if (!pBuffer)
                {
                    break;
                }
                cbBuffer = cbData;
            }

            err = RegQueryValueEx(  hMiniKey,
                                    szValue,
                                    0,
                                    &dwType,
                                    pBuffer,
                                    &cbData);

            if (err == 0)
            {
                pPacked = (PSerializedMiniAccount) pBuffer;

                if (pPacked->Version != MINI_VERSION)
                {
                    continue;
                }

                pAccount = LocalAlloc(LMEM_FIXED, sizeof(MiniAccount));
                if (pAccount)
                {
                    pAccount->Flags = pPacked->Flags;
                    pAccount->IconId = pPacked->IconId;
                    pAccount->pszUsername = LocalAlloc(LMEM_FIXED, (cbValue+1)*sizeof(TCHAR));
                    if (pAccount->pszUsername)
                    {
                        wcscpy(pAccount->pszUsername, szValue);
                    }

                    pAccount->pszDomain = LocalAlloc(LMEM_FIXED, pPacked->dwDomainLength);
                    if (pAccount->pszDomain)
                    {
                        wcscpy(pAccount->pszDomain,
                               (PWSTR) ((pBuffer) + pPacked->dwDomainOffset) );
                    }

                    pAccount->pszPassword = LocalAlloc(LMEM_FIXED, pPacked->dwPasswordLength);
                    if (pAccount->pszPassword)
                    {
                        wcscpy(pAccount->pszPassword,
                               (PWSTR) (pBuffer + pPacked->dwPasswordOffset) );
                    }

                    pAccount->pszComment = LocalAlloc(LMEM_FIXED, pPacked->dwCommentLength);
                    if (pAccount->pszComment)
                    {
                        wcscpy(pAccount->pszComment,
                               (PWSTR) (pBuffer + pPacked->dwCommentOffset) );
                    }

                    pAccount->pNext = pAccountList;
                    pAccountList = pAccount;
                }
            }

        }

    }

    return(TRUE);
}

VOID
InitializeImageLists()
{
    HICON   hIcon;

    hiLogonSmall = ImageList_Create(16, 16, TRUE, 4, 0);
    hiLogonLarge = ImageList_Create(32, 32, TRUE, 4, 0);

    hIcon = LoadIcon(hDllInstance, MAKEINTRESOURCE(IDI_USER_ICON));
    if (!hIcon)
    {
        DebugLog((DEB_ERROR, "Unable to load icon, %d\n", GetLastError()));
    }
    ImageList_AddIcon(hiLogonLarge, hIcon);
    ImageList_AddIcon(hiLogonSmall, hIcon);

    hIcon = LoadIcon(hDllInstance, MAKEINTRESOURCE(IDI_NEW_USER_ICON));
    ImageList_AddIcon(hiLogonLarge, hIcon);
    ImageList_AddIcon(hiLogonSmall, hIcon);

}

PopulateListView(
    HWND            hLV,
    PMiniAccount    pAccList)
{
    LV_ITEM     lvi;
    LV_COLUMN   lvc;
    DWORD       Count;

    ListView_SetImageList(hLV, hiLogonLarge, LVSIL_NORMAL);
    ListView_SetImageList(hLV, hiLogonSmall, LVSIL_SMALL);

    //
    // Ok, now set up the columns for the list view
    //

    lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
    lvc.fmt = LVCFMT_LEFT;
    lvc.cx = 0;

    lvc.iSubItem = 0;
    lvc.pszText = TEXT("Name        ");
    ListView_InsertColumn(hLV, 0, &lvc);

    lvc.iSubItem = 1;
    lvc.pszText = TEXT("Domain   ");
    ListView_InsertColumn(hLV, 1, &lvc);
    //
    // Comment
    //

    lvc.iSubItem = 2;
    lvc.pszText = TEXT("Comment   ");
    ListView_InsertColumn(hLV, 2, &lvc);

    lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;

    Count = 0;
    while (pAccList)
    {
        lvi.iItem = Count;
        lvi.iSubItem  = 0;
        lvi.iImage = (pAccList->Flags & MINI_NEW_ACCOUNT) ? 1 : 0;
        lvi.pszText = pAccList->pszUsername;
        lvi.lParam = (LPARAM) pAccList;

        ListView_InsertItem(hLV, &lvi);

        ListView_SetItemText(hLV, Count, 1, pAccList->pszDomain);
        ListView_SetItemText(hLV, Count, 2, pAccList->pszComment);

        Count++;
        pAccList = pAccList->pNext;

    }

    return(TRUE);
}

int
CALLBACK
NewUserDlgProc(
    HWND    hDlg,
    UINT    Message,
    WPARAM  wParam,
    LPARAM  lParam)
{
    PGlobals        pGlobals;
    PMiniAccount    pMini;

    pGlobals = (PGlobals) GetWindowLong(hDlg, GWL_USERDATA);
    switch (Message)
    {
        case WM_INITDIALOG:
            CenterWindow(hDlg);
            SetWindowLong(hDlg, GWL_USERDATA, lParam);
            return(TRUE);

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                pMini = LocalAlloc(LMEM_FIXED, sizeof(MiniAccount));
                pMini->pszUsername = AllocAndCaptureText(hDlg, IDD_USER_NAME);
                pMini->pszDomain = AllocAndCaptureText(hDlg, IDD_DOMAIN);
                pMini->pszPassword = AllocAndCaptureText(hDlg, IDD_PASSWORD);
                pMini->pszComment = DupString(TEXT(""));
                pMini->Flags = MINI_SAVE | MINI_CAN_EDIT;
                pMini->IconId = 0;
                pMini->pNext = pAccountList;
                pAccountList = pMini;
                pGlobals->pAccount = pMini;
                EndDialog(hDlg, IDOK);
            }
            if (LOWORD(wParam) == IDCANCEL)
            {
                EndDialog(hDlg, IDCANCEL);
            }
            return(TRUE);
    }

    return(FALSE);

}

LogonDlgInit(
    HWND    hDlg,
    LPARAM  lParam)
{
    PGlobals        pGlobals;
    HWND            hLV;

    pGlobals = (PGlobals) lParam;
    SetWindowLong(hDlg, GWL_USERDATA, lParam);
    pGlobals->pAccount = NULL;

    if (pAccountList == NULL)
    {
        LoadMiniAccounts(pGlobals);
    }

    InitializeImageLists();

    hLV = GetDlgItem(hDlg, IDD_LOGON_LV);
    PopulateListView(hLV, pAccountList);

    CenterWindow(hDlg);

    ListView_SetColumnWidth(hLV, 0, LVSCW_AUTOSIZE);
    ListView_SetColumnWidth(hLV, 1, LVSCW_AUTOSIZE);
    ListView_SetColumnWidth(hLV, 2, LVSCW_AUTOSIZE);

    ShowWindow(hLV, SW_NORMAL);
    EnableWindow(hLV, TRUE);
    EnableWindow(GetDlgItem(hDlg, IDD_LOGON_BUTTON), FALSE);

    return(TRUE);

}

int
HandleLvNotify(
    HWND        hDlg,
    PGlobals    pGlobals,
    NMHDR *     pNMH)
{
    NM_LISTVIEW *   pNotify;
    LV_ITEM         lvi;
    HWND            hLV;
    PMiniAccount *  ppAcc;
    int             ret;
    int             index;

    pNotify = (NM_LISTVIEW *) pNMH;

    hLV = GetDlgItem(hDlg, IDD_LOGON_LV);

    ppAcc = &pGlobals->pAccount;

    switch (pNotify->hdr.code)
    {
        case NM_CLICK:
        case NM_DBLCLK:
            EnableWindow(GetDlgItem(hDlg, IDD_LOGON_BUTTON), TRUE);

            index = ListView_GetNextItem(hLV, -1, LVNI_SELECTED);
            if (index >= 0)
            {
                lvi.iItem = index;
                lvi.iSubItem = 0;
                lvi.mask = LVIF_PARAM;

                ret = ListView_GetItem(hLV, &lvi);
                *ppAcc = (PMiniAccount) lvi.lParam;
                DebugLog((DEB_TRACE, "Selected Item %d, lParam = %x\n", index, lvi.lParam));
            }

            if (pNotify->hdr.code == NM_DBLCLK)
            {
                PostMessage(hDlg, WM_COMMAND, IDOK, 0);
            }
            return(TRUE);

    }
    return(FALSE);

}

int
CALLBACK
LogonDlgProc(
    HWND        hDlg,
    UINT        Message,
    WPARAM      wParam,
    LPARAM      lParam)
{
    NMHDR *     pNotifyHeader;
    PGlobals    pGlobals;
    int         result;

    pGlobals = (PGlobals) GetWindowLong(hDlg, GWL_USERDATA);
    switch (Message)
    {
        case WM_INITDIALOG:
            return(LogonDlgInit(hDlg, lParam));

        case WM_COMMAND:
            if (LOWORD(wParam) == IDCANCEL)
            {
                EndDialog(hDlg, WLX_SAS_ACTION_NONE);
            }
            if (LOWORD(wParam) == IDD_LOGON_BUTTON)
            {
                if (pGlobals->pAccount->Flags & MINI_NEW_ACCOUNT)
                {
                    result = pWlxFuncs->WlxDialogBoxParam(  hGlobalWlx,
                                                            hDllInstance,
                                                            (LPTSTR) MAKEINTRESOURCE(IDD_NEW_USER_LOGON),
                                                            hDlg,
                                                            (DLGPROC) NewUserDlgProc,
                                                            (LPARAM) pGlobals);
                }
                else
                {
                    result = IDOK;
                }

                if (result == IDOK)
                {
                    EndDialog(hDlg, WLX_SAS_ACTION_LOGON);
                }
            }
            if (LOWORD(wParam) == IDD_SHUTDOWN_BUTTON)
            {
                result = pWlxFuncs->WlxDialogBoxParam(  hGlobalWlx,
                                                        hDllInstance,
                                                        (LPTSTR) MAKEINTRESOURCE(IDD_SHUTDOWN),
                                                        hDlg,
                                                        (DLGPROC) ShutdownDlgProc,
                                                        (LPARAM) pGlobals);
                if (result != WLX_SAS_ACTION_NONE)
                {
                    EndDialog(hDlg, result);
                }
            }
            return(TRUE);
            break;

        case WM_NOTIFY:
            pNotifyHeader = (NMHDR *) lParam;
            if (wParam == IDD_LOGON_LV)
            {
                return(HandleLvNotify(hDlg, pGlobals, pNotifyHeader));
            }
        case WM_CLOSE:
            hiLogonSmall = NULL;
            hiLogonLarge = NULL;
            return(TRUE);

    }

    return(FALSE);
}

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

int
AttemptLogon(
    PGlobals        pGlobals,
    PMiniAccount    pAccount,
    PSID            pLogonSid,
    PLUID           pLogonId)
{
    HANDLE              hUser;
    TOKEN_STATISTICS    TStats;
    TOKEN_GROUPS    *   pGroups;
    DWORD               size;
    DWORD               i;

    if (LogonUser(  pAccount->pszUsername,
                    pAccount->pszDomain,
                    pAccount->pszPassword,
                    LOGON32_LOGON_INTERACTIVE,
                    LOGON32_PROVIDER_DEFAULT,
                    &hUser))
    {
        if (pAccount->Flags & MINI_SAVE)
        {
            SaveMiniAccount(pAccount);
            pAccount->Flags &= ~MINI_SAVE;
        }

        pGlobals->hUserToken = hUser;

        //
        // Now, grovel the token we got back for interesting stuff:
        //

        GetTokenInformation(hUser,
                            TokenStatistics,
                            &TStats,
                            sizeof(TStats),
                            &size);

        *pLogonId = TStats.AuthenticationId;

        pGroups = LocalAlloc(LMEM_FIXED, 1024);

        if (!pGroups)
        {
            CloseHandle(hUser);
            return(WLX_SAS_ACTION_NONE);
        }

        //
        // The tricky part.  We need to get the Logon SID from the token,
        // since that is what Winlogon will use to protect the windowstation
        // and desktop.
        //

        GetTokenInformation(hUser,
                            TokenGroups,
                            pGroups,
                            1024,
                            &size);

        if (size > 1024)
        {
            pGroups = LocalReAlloc(pGroups, LMEM_FIXED, size);
            GetTokenInformation(hUser,
                                TokenGroups,
                                pGroups,
                                size,
                                &size);
        }

        for (i = 0; i < pGroups->GroupCount ; i++)
        {
            if ((pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
            {
                CopySid(GetLengthSid(pLogonSid),
                        pLogonSid,
                        pGroups->Groups[i].Sid );
                break;
            }
        }

        LocalFree(pGroups);

        return(WLX_SAS_ACTION_LOGON);
    }

    return(WLX_SAS_ACTION_NONE);
}


OPTIONS.C

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       options.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    5-02-95   RichardW   Created
//
//----------------------------------------------------------------------------

#include "gina.h"
#pragma hdrstop

typedef
NET_API_STATUS (NET_API_FUNCTION * NUCP)(LPWSTR, LPWSTR, LPWSTR, LPWSTR);

NUCP    NetUserChangePasswordFn = NULL;
HMODULE hNetApi32;

BOOL
LoadNetapi(HWND hDlg)
{
    hNetApi32 = LoadLibrary(TEXT("netapi32.dll"));
    if (hNetApi32)
    {
        NetUserChangePasswordFn = (NUCP) GetProcAddress(hNetApi32, "NetUserChangePassword");
        if (NetUserChangePasswordFn)
        {
            return(TRUE);
        }
    }

    ErrorMessage(hDlg, TEXT("Change Password"), MB_ICONSTOP | MB_OK);

    return(FALSE);

}

PWSTR
AllocAndCaptureText(
    HWND    hDlg,
    int     Id)
{
    WCHAR   szTemp[MAX_PATH];
    PWSTR   New;
    DWORD   cb;

    cb = GetDlgItemText(hDlg, Id, szTemp, MAX_PATH);
    New = LocalAlloc(LMEM_FIXED, (cb + 1) * sizeof(WCHAR));
    if (New)
    {
        wcscpy(New, szTemp);
    }
    return(New);
}

PWSTR
DupString(
    PWSTR   pszText)
{
    PWSTR   New;
    DWORD   cb;

    cb = (wcslen(pszText) + 1) * sizeof(WCHAR);
    New = LocalAlloc(LMEM_FIXED, cb);

    if (New)
    {
        wcscpy(New, pszText);
    }

    return(New);
}

BOOL
TryToChangePassword(
    HWND        hDlg,
    PGlobals    pGlobals)
{
    PWSTR   pszUsername;
    PWSTR   pszDomain;
    PWSTR   pszOld;
    PWSTR   pszNew;
    PWSTR   pszTemp;

    NET_API_STATUS  NetStatus;

    if (!NetUserChangePasswordFn)
    {
        if (!LoadNetapi(hDlg))
        {
            return(FALSE);
        }
    }

    pszUsername = AllocAndCaptureText(hDlg, IDD_USER_NAME);
    pszDomain = AllocAndCaptureText(hDlg, IDD_DOMAIN);
    pszOld = AllocAndCaptureText(hDlg, IDD_OLD_PASSWORD);
    pszNew = AllocAndCaptureText(hDlg, IDD_NEW_PASSWORD);

    if (!pszUsername || !pszDomain || !pszOld || !pszNew)
    {
        goto clean_exit;
    }

    pszTemp = AllocAndCaptureText(hDlg, IDD_CONFIRM_PASSWORD);
    if (wcscmp(pszNew, pszTemp))
    {
        LocalFree(pszTemp);
        MessageBox(hDlg, TEXT("Your passwords did not match."), TEXT("Change Password"),
                        MB_ICONSTOP | MB_OK);
        goto clean_exit;
    }

    NetStatus = NetUserChangePasswordFn(pszDomain, pszUsername, pszOld, pszNew);
    if (NetStatus != NERR_Success)
    {
        SetLastError(NetStatus);
        ErrorMessage(hDlg, TEXT("Change Password"), MB_ICONSTOP | MB_OK);
    }
    else
        MessageBox(hDlg, TEXT("Your password was changed successfully"),
                    TEXT("Change Password"), MB_ICONINFORMATION | MB_OK);

clean_exit:
    if (pszUsername)
    {
        LocalFree(pszUsername);
    }
    if (pszDomain)
    {
        LocalFree(pszDomain);
    }
    if (pszOld)
    {
        LocalFree(pszOld);
    }
    if (pszNew)
    {
        LocalFree(pszNew);
    }

    return(NetStatus == NERR_Success);
}

int
CALLBACK
ChangePasswordDlgProc(
    HWND        hDlg,
    UINT        Message,
    WPARAM      wParam,
    LPARAM      lParam)
{
    PGlobals    pGlobals;

    pGlobals = (PGlobals) GetWindowLong(hDlg, GWL_USERDATA);
    switch (Message)
    {
        case WM_INITDIALOG:
            CenterWindow(hDlg);
            SetWindowLong(hDlg, GWL_USERDATA, lParam);
            return(TRUE);

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                if (TryToChangePassword(hDlg, pGlobals))
                {
                    SetWindowText(GetDlgItem(hDlg, IDCANCEL), TEXT("Done"));
                }
                SetDlgItemText(hDlg, IDD_OLD_PASSWORD, TEXT(""));
                SetDlgItemText(hDlg, IDD_NEW_PASSWORD, TEXT(""));
                SetDlgItemText(hDlg, IDD_CONFIRM_PASSWORD, TEXT(""));
                SetFocus(GetDlgItem(hDlg, IDD_OLD_PASSWORD));

                return(TRUE);
            }
            if (LOWORD(wParam) == IDCANCEL)
            {
                EndDialog(hDlg, 0);
                return(TRUE);
            }

    }
    return(FALSE);
}

int
CALLBACK
ConfigDlgProc(
    HWND        hDlg,
    UINT        Message,
    WPARAM      wParam,
    LPARAM      lParam)
{
    PGlobals    pGlobals;

    pGlobals = (PGlobals) GetWindowLong(hDlg, GWL_USERDATA);
    switch (Message)
    {
        case WM_INITDIALOG:
            pGlobals = (PGlobals) lParam;

            CenterWindow(hDlg);
            SetWindowLong(hDlg, GWL_USERDATA, lParam);

            CheckDlgButton(hDlg, IDD_NO_NEW_USERS, !pGlobals->fAllowNewUser);

            CheckDlgButton(hDlg, IDD_AUTO_LOGON,
                    (pGlobals->pAccount->Flags & MINI_AUTO_LOGON) ? 1 : 0 );

            return(TRUE);

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDOK:
                    pGlobals->fAllowNewUser = !IsDlgButtonChecked(hDlg, IDD_NO_NEW_USERS);
                    if (IsDlgButtonChecked(hDlg, IDD_AUTO_LOGON))
                    {
                        pGlobals->pAccount->Flags |= MINI_AUTO_LOGON;
                    }
                    else
                    {
                        pGlobals->pAccount->Flags &= ~MINI_AUTO_LOGON;
                    }
                    EndDialog(hDlg, IDOK);
                    return(TRUE);

                case IDCANCEL:
                    EndDialog(hDlg, IDCANCEL);
                    return(TRUE);

            }
    }

    return(FALSE);

}

int
InitOptionsDialog(
    HWND        hDlg,
    LPARAM      lParam)
{
    CenterWindow(hDlg);
    SetWindowLong(hDlg, GWL_USERDATA, lParam);

    return(1);
}

int
CALLBACK
OptionsDlgProc(
    HWND        hDlg,
    UINT        Message,
    WPARAM      wParam,
    LPARAM      lParam)
{
    PGlobals    pGlobals;
    int         result;

    pGlobals = (PGlobals) GetWindowLong(hDlg, GWL_USERDATA);

    switch (Message)
    {
        case WM_INITDIALOG:
            return InitOptionsDialog(hDlg, lParam);

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDCANCEL:
                    EndDialog(hDlg, WLX_SAS_ACTION_NONE);
                    return(TRUE);

                case IDD_LOCK_BUTTON:
                    EndDialog(hDlg, WLX_SAS_ACTION_LOCK_WKSTA);
                    return(TRUE);

                case IDD_TASK_BUTTON:
                    EndDialog(hDlg, WLX_SAS_ACTION_TASKLIST);
                    return(TRUE);

                case IDD_OPTIONS_EXIT:
                    result = pWlxFuncs->WlxDialogBoxParam(  hGlobalWlx,
                                                        hDllInstance,
                                                        (LPTSTR) MAKEINTRESOURCE(IDD_SHUTDOWN),
                                                        hDlg,
                                                        ShutdownDlgProc,
                                                        (LONG) pGlobals);
                    if (result != WLX_SAS_ACTION_NONE)
                    {
                        EndDialog(hDlg, result);
                    }
                    return(TRUE);

                case IDD_PASSWORD_BUTTON:
                    pWlxFuncs->WlxDialogBoxParam(   hGlobalWlx,
                                                    hDllInstance,
                                                    (LPTSTR) MAKEINTRESOURCE(IDD_CHANGE_PASSWORD),
                                                    hDlg,
                                                    ChangePasswordDlgProc,
                                                    (LONG) pGlobals);
                    return(TRUE);

                case IDD_CONFIG_BUTTON:
                    pWlxFuncs->WlxDialogBoxParam(   hGlobalWlx,
                                                    hDllInstance,
                                                    (LPTSTR) MAKEINTRESOURCE(IDD_LOGON_CONFIG),
                                                    hDlg,
                                                    ConfigDlgProc,
                                                    (LONG) pGlobals);
                    return(TRUE);

            }
    }
    return(FALSE);
}


OUT.C

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       out.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-20-95   RichardW   Created
//
//----------------------------------------------------------------------------

#include "gina.h"

PMiniAccount    pAccountList = NULL;

BOOL
InitializeAccountList(
    VOID)
{
    PMiniAccount
    //
    //
}


PROTOS.H

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       protos.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-20-95   RichardW   Created
//
//----------------------------------------------------------------------------

int
CALLBACK
WelcomeDlgProc(
    HWND        hDlg,
    UINT        Message,
    WPARAM      wParam,
    LPARAM      lParam);

int
CALLBACK
LogonDlgProc(
    HWND        hDlg,
    UINT        Message,
    WPARAM      wParam,
    LPARAM      lParam);

int
CALLBACK
ShutdownDlgProc(
    HWND        hDlg,
    UINT        Message,
    WPARAM      wParam,
    LPARAM      lParam);

int
CALLBACK
OptionsDlgProc(
    HWND        hDlg,
    UINT        Message,
    WPARAM      wParam,
    LPARAM      lParam);

VOID
CenterWindow(
    HWND    hwnd
    );

int
ErrorMessage(
    HWND        hWnd,
    PWSTR       pszTitleBar,
    DWORD       Buttons);

int
AttemptLogon(
    PGlobals        pGlobals,
    PMiniAccount    pAccount,
    PSID            pLogonSid,
    PLUID           pLogonId);

PWSTR
AllocAndCaptureText(
    HWND    hDlg,
    int     Id);

PWSTR
DupString(
    PWSTR   pszText);


DEBUG.C

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       debug.c
//
//  Contents:   Debugging support functions
//
//  Classes:
//
//  Functions:
//
//  Note:       This file is not compiled for retail builds
//
//  History:    4-29-93   RichardW   Created
//
//----------------------------------------------------------------------------

//
//  For ease of debugging the SPMgr, all the debug support functions have
//  been stuck here.  Basically, we read info from win.ini, since that allows
//  us to configure the debug level via a text file (and DOS, for example).
//
//  Format is:
//
//  win.ini
//
//  [Gina]
//      DebugFlags=<Flag>[<,Flag>]*
//
//  WHERE:
//      Flag is one of the following:
//          Error, Warning, Trace
//

#if DBG         // NOTE:  This file not compiled for retail builds

#include "gina.h"
#include <stdio.h>
#include <wchar.h>

FILE *  LogFile;
DWORD   GINAInfoLevel = 3;

// Debugging support functions.

// These two functions do not exist in Non-Debug builds.  They are wrappers
// to the commnot functions (maybe I should get rid of that as well...)
// that echo the message to a log file.

char   szSection[] = "Gina";
char * DebLevel[] = {"GINA-Error", "GINA-Warn", "GINA-Trace"
                    };

typedef struct _DebugKeys {
    char *  Name;
    DWORD   Value;
} DebugKeys, *PDebugKeys;

DebugKeys   DebugKeyNames[] = {
                {"Error",       DEB_ERROR},
                {"Warning",     DEB_WARN},
                {"Trace",       DEB_TRACE},
                };

#define NUM_DEBUG_KEYS  sizeof(DebugKeyNames) / sizeof(DebugKeys)
#define NUM_BREAK_KEYS  sizeof(BreakKeyNames) / sizeof(DebugKeys)

//+---------------------------------------------------------------------------
//
//  Function:   LogEvent
//
//  Synopsis:   Logs an event to the console and, optionally, a file.
//
//  Effects:
//
//  Arguments:  [Mask]   --
//              [Format] --
//              [Format] --
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    4-29-93   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

void
LogEvent(   long            Mask,
            const char *    Format,
            ...)
{
    va_list ArgList;
    int     Level = 0;
    int     PrefixSize = 0;
    char    szOutString[256];
    long    OriginalMask = Mask;

    if (Mask & GINAInfoLevel)
    {
        while (!(Mask & 1))
        {
            Level++;
            Mask >>= 1;
        }
        if (Level >= (sizeof(DebLevel) / sizeof(char *)) )
        {
            Level = (sizeof(DebLevel) / sizeof(char *)) - 1;
        }

        //
        // Make the prefix first:  "Process.Thread> GINA-XXX"
        //

        PrefixSize = sprintf(szOutString, "%d.%d> %s: ",
                GetCurrentProcessId(), GetCurrentThreadId(), DebLevel[Level]);

        va_start(ArgList, Format);

        if (_vsnprintf(&szOutString[PrefixSize], sizeof(szOutString) - PrefixSize,
                            Format, ArgList) < 0)
        {
            //
            // Less than zero indicates that the string could not be
            // fitted into the buffer.  Output a special message indicating
            // that:
            //

            OutputDebugStringA("GINA!LogEvent:  Could not pack string into 256 bytes\n");

        }
        else
        {
            OutputDebugStringA(szOutString);
        }

        if (LogFile)
        {
            SYSTEMTIME  stTime;

            GetLocalTime(&stTime);
            fprintf(LogFile, "%02d:%02d:%02d.%03d: %s\n",
                    stTime.wHour, stTime.wMinute, stTime.wSecond,
                    stTime.wMilliseconds, szOutString);

            fflush(LogFile);
        }

    }

}

void
OpenLogFile(LPSTR   pszLogFile)
{
    LogFile = fopen(pszLogFile, "a");
    if (!LogFile)
    {
        OutputDebugStringA("GINA: Could not open logfile for append");
        OutputDebugStringA(pszLogFile);
    }
    DebugLog((DEB_TRACE, "Log file '%s' begins\n", pszLogFile));
}

DWORD
GetDebugKeyValue(
    PDebugKeys      KeyTable,
    int             cKeys,
    LPSTR           pszKey)
{
    int     i;

    for (i = 0; i < cKeys ; i++ )
    {
        if (strcmpi(KeyTable[i].Name, pszKey) == 0)
        {
            return(KeyTable[i].Value);
        }
    }
    return(0);
}

//+---------------------------------------------------------------------------
//
//  Function:   LoadDebugParameters
//
//  Synopsis:   Loads debug parameters from win.ini
//
//  Effects:
//
//  Arguments:  (none)
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    4-29-93   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

void
LoadDebugParameters(void)
{
    char    szVal[128];
    char *  pszDebug;
    int     cbVal;

    cbVal = GetProfileStringA(szSection, "DebugFlags", "Error,Warning", szVal, sizeof(szVal));

    pszDebug = strtok(szVal, ", \t");
    while (pszDebug)
    {
        GINAInfoLevel |= GetDebugKeyValue(DebugKeyNames, NUM_DEBUG_KEYS, pszDebug);
        pszDebug = strtok(NULL, ", \t");
    }

    cbVal = GetProfileStringA(szSection, "LogFile", "", szVal, sizeof(szVal));
    if (cbVal)
    {
        OpenLogFile(szVal);
    }

}

//+---------------------------------------------------------------------------
//
//  Function:   InitDebugSupport
//
//  Synopsis:   Initializes debugging support for the GINAgr
//
//  Effects:
//
//  Arguments:  (none)
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    4-29-93   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

void
InitDebugSupport(void)
{
    LoadDebugParameters();

}

#else // DBG

#pragma warning(disable:4206)   // Disable the empty transation unit
                                // warning/error

#endif  // NOTE:  This file not compiled for retail builds


RESIDS.H

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       resids.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-28-95   RichardW   Created
//
//----------------------------------------------------------------------------

#define IDI_USER_ICON       10
#define IDI_NEW_USER_ICON   11


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by res.rc
//
#define IDD_WELCOME_DIALOG              101
#define IDD_LOGON_DIALOG                102
#define IDD_LOGON_LV                    1001
#define IDD_LOGON                       1002
#define IDD_SHUTDOWN                    1003
#define IDD_WELCOME_TEXT                -1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        105
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1004
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SHUTDOWN.C

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       shutdown.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-28-95   RichardW   Created
//
//----------------------------------------------------------------------------

#include "gina.h"
#pragma hdrstop

WCHAR   szShutdownSettingPath[] = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Shutdown");
int
ShutdownDialogInit(
    HWND        hDlg,
    LPARAM      lParam)
{
    PGlobals    pGlobals;
    HKEY        hKey;
    DWORD       dwValue;
    DWORD       dwSize;
    DWORD       dwType;
    DWORD       PowerOff;

    pGlobals = (PGlobals) lParam;

    SetWindowLong(hDlg, GWL_USERDATA, lParam);

    PowerOff = GetProfileInt(TEXT("Winlogon"), TEXT("PowerDownAfterShutdown"), 0);

    if (pGlobals->hUserToken)
    {
        ImpersonateLoggedOnUser(pGlobals->hUserToken);
    }

    dwValue = 0;

    if (!RegOpenKey(HKEY_CURRENT_USER,
                    szShutdownSettingPath,
                    &hKey))
    {
        dwSize = sizeof(DWORD);
        RegQueryValueEx(hKey, TEXT("Shutdown Setting"), 0, &dwType, (PBYTE) &dwValue, &dwSize);
        RegCloseKey(hKey);
    }

    if (pGlobals->hUserToken)
    {
        RevertToSelf();
    }

    if (!PowerOff)
    {
        ShowWindow(GetDlgItem(hDlg, IDD_CONFIRM_POWEROFF), SW_HIDE);
    }

    if (!pGlobals->hUserToken)
    {
        ShowWindow(GetDlgItem(hDlg, IDD_CONFIRM_LOGOFF), SW_HIDE);
    }

    switch (dwValue)
    {
        case 0:
            if (pGlobals->hUserToken)
            {
                CheckDlgButton(hDlg, IDD_CONFIRM_LOGOFF, 1);
                SetFocus(GetDlgItem(hDlg, IDD_CONFIRM_LOGOFF));
                break;
            }

        case 2:
            CheckDlgButton(hDlg, IDD_CONFIRM_REBOOT, 1);
                SetFocus(GetDlgItem(hDlg, IDD_CONFIRM_REBOOT));
            break;
        case 3:
            if (PowerOff)
            {
                CheckDlgButton(hDlg, IDD_CONFIRM_POWEROFF, 1);
                SetFocus(GetDlgItem(hDlg, IDD_CONFIRM_POWEROFF));
                break;
            }
        default:
            CheckDlgButton(hDlg, IDD_CONFIRM_SHUTDOWN, 1);
            SetFocus(GetDlgItem(hDlg, IDD_CONFIRM_SHUTDOWN));

    }

    CenterWindow(hDlg);

    SetFocus(GetDlgItem(hDlg, IDOK));

    return(1);

}

VOID
UpdateShutdownSettings(
            PGlobals    pGlobals,
            DWORD       Setting)
{
    // int err;
    HKEY    hKey;
    DWORD   Actual;

    switch (Setting)
    {
        default:
        case IDD_CONFIRM_LOGOFF:
            Actual = 0;
            break;

        case IDD_CONFIRM_SHUTDOWN:
            Actual = 1;
            break;

        case IDD_CONFIRM_REBOOT:
            Actual = 2;
            break;

        case IDD_CONFIRM_POWEROFF:
            Actual = 3;
            break;
    }

    if (pGlobals->hUserToken)
    {
        ImpersonateLoggedOnUser(pGlobals->hUserToken);
    }

    if (!RegOpenKey(HKEY_CURRENT_USER,
                    szShutdownSettingPath,
                    &hKey))
    {
        RegSetValueEx(  hKey,
                        TEXT("Shutdown Setting"),
                        0,
                        REG_DWORD,
                        (PBYTE) &Actual,
                        sizeof(DWORD) );
        RegCloseKey(hKey);
    }

    if (pGlobals->hUserToken)
    {
        RevertToSelf();
    }

}

int
CALLBACK
ShutdownDlgProc(
    HWND        hDlg,
    UINT        Message,
    WPARAM      wParam,
    LPARAM      lParam)
{
    PGlobals    pGlobals;

    switch (Message)
    {
        case WM_INITDIALOG:
            return(ShutdownDialogInit(hDlg, lParam));

        case WM_COMMAND:
            if (LOWORD(wParam) == IDCANCEL)
            {
                EndDialog(hDlg, WLX_SAS_ACTION_NONE);
            }
            if (LOWORD(wParam) == IDOK)
            {
                pGlobals = (PGlobals) GetWindowLong(hDlg, GWL_USERDATA);

                if (IsDlgButtonChecked(hDlg, IDD_CONFIRM_LOGOFF))
                {
                    UpdateShutdownSettings(pGlobals, IDD_CONFIRM_LOGOFF);
                    EndDialog(hDlg, WLX_SAS_ACTION_LOGOFF);
                }
                else if (IsDlgButtonChecked(hDlg, IDD_CONFIRM_REBOOT))
                {
                    UpdateShutdownSettings(pGlobals, IDD_CONFIRM_REBOOT);
                    EndDialog(hDlg, WLX_SAS_ACTION_SHUTDOWN_REBOOT);
                }
                else if (IsDlgButtonChecked(hDlg, IDD_CONFIRM_POWEROFF))
                {
                    UpdateShutdownSettings(pGlobals, IDD_CONFIRM_POWEROFF);
                    EndDialog(hDlg, WLX_SAS_ACTION_SHUTDOWN_POWER_OFF);
                }
                else
                {
                    UpdateShutdownSettings(pGlobals, IDD_CONFIRM_SHUTDOWN);
                    EndDialog(hDlg, WLX_SAS_ACTION_SHUTDOWN);
                }

            }
            return(TRUE);
    }
    return(FALSE);
}


STRUCTS.H

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       structs.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-20-95   RichardW   Created
//
//----------------------------------------------------------------------------

typedef struct _MiniAccount {
    struct _MiniAccount *   pNext;
    PWSTR                   pszUsername;
    PWSTR                   pszDomain;
    PWSTR                   pszPassword;
    PWSTR                   pszComment;
    DWORD                   IconId;
    DWORD                   Flags;
} MiniAccount, * PMiniAccount;

typedef struct _SerializedMiniAccount {
    DWORD                   Version;
    DWORD                   dwDomainOffset;
    DWORD                   dwDomainLength;
    DWORD                   dwPasswordOffset;
    DWORD                   dwPasswordLength;
    DWORD                   dwCommentOffset;
    DWORD                   dwCommentLength;
    DWORD                   Flags;
    DWORD                   IconId;
} SerializedMiniAccount, * PSerializedMiniAccount;

#define MINI_VERSION            0

#define MINI_PASSWORD_REQUIRED  0x00000001
#define MINI_PASSWORD_ALWAYS    0x00000002
#define MINI_NEW_ACCOUNT        0x00000004
#define MINI_CAN_EDIT           0x00000008
#define MINI_AUTO_LOGON         0x00000010
#define MINI_SAVE               0x00000020

typedef struct _Globals {
    BOOL                    fAllowNewUser;
    BOOL                    fAutoLogonAtBoot;
    BOOL                    fAutoLogonAlways;
    HANDLE                  hUserToken;
    PMiniAccount            pAccount;
} Globals, * PGlobals;


UTIL.C

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       util.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-20-95   RichardW   Created
//
//----------------------------------------------------------------------------

#include "gina.h"
#pragma hdrstop

HMODULE hNetMsg = NULL;

//+---------------------------------------------------------------------------
//
//  Function:   CenterWindow
//
//  Synopsis:   Centers a window
//
//  Arguments:  [hwnd] --
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
CenterWindow(
    HWND    hwnd
    )
{
    RECT    rect;
    LONG    dx, dy;
    LONG    dxParent, dyParent;
    LONG    Style;

    // Get window rect
    GetWindowRect(hwnd, &rect);

    dx = rect.right - rect.left;
    dy = rect.bottom - rect.top;

    // Get parent rect
    Style = GetWindowLong(hwnd, GWL_STYLE);
    if ((Style & WS_CHILD) == 0) {

        // Return the desktop windows size (size of main screen)
        dxParent = GetSystemMetrics(SM_CXSCREEN);
        dyParent = GetSystemMetrics(SM_CYSCREEN);
    } else {
        HWND    hwndParent;
        RECT    rectParent;

        hwndParent = GetParent(hwnd);
        if (hwndParent == NULL) {
            hwndParent = GetDesktopWindow();
        }

        GetWindowRect(hwndParent, &rectParent);

        dxParent = rectParent.right - rectParent.left;
        dyParent = rectParent.bottom - rectParent.top;
    }

    // Centre the child in the parent
    rect.left = (dxParent - dx) / 2;
    rect.top  = (dyParent - dy) / 3;

    // Move the child into position
    SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, 0, 0, SWP_NOSIZE);

    SetForegroundWindow(hwnd);
}

int
ErrorMessage(
    HWND        hWnd,
    PWSTR       pszTitleBar,
    DWORD       Buttons)
{
    WCHAR   szMessage[256];
    DWORD   GLE;

    GLE = GetLastError();

    if (GLE >= NERR_BASE)
    {
        if (!hNetMsg)
        {
            hNetMsg = LoadLibrary(TEXT("netmsg.dll"));
        }
        FormatMessage(
            FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
            hNetMsg,                               // ignored
            GLE,                                  // message id
            MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),   // message language
            szMessage,                  // address of buffer pointer
            199,                                  // minimum buffer size
            NULL );                              // no other arguments

    }

    FormatMessage(
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,                               // ignored
            (GetLastError()),                     // message id
            MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),   // message language
            szMessage,                  // address of buffer pointer
            199,                                  // minimum buffer size
            NULL );                              // no other arguments

    return(MessageBox(hWnd, szMessage, pszTitleBar, Buttons));

}


WELCOME.C

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       welcome.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-20-95   RichardW   Created
//
//----------------------------------------------------------------------------

#include "gina.h"

int
CALLBACK
WelcomeDlgProc(
    HWND        hDlg,
    UINT        Message,
    WPARAM      wParam,
    LPARAM      lParam)
{
    switch (Message)
    {
        case WM_INITDIALOG:
            CenterWindow(hDlg);
            return(TRUE);

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                pWlxFuncs->WlxSasNotify(hGlobalWlx, GINA_SAS_1);
            }
            return(TRUE);

        default:
            return(FALSE);
    }

}


README.TXT

This sample illustrates a pass-thru "stub" gina which can be used
in some cases to simplify gina development.

A common use for a gina is to implement code which requires the
credentials of the user logging onto the workstation.  The credentials
may be required for syncronization with foreign account databases
or custom authentication activities.

In this example case, it is possible to implement a simple gina
stub layer which simply passes control for the required functions
to the previously installed gina, and captures the interesting
parameters from that gina.  In this scenario, the existing functionality
in the existent gina is retained.  In addition, the development time
is reduced drastically, as existing functionality does not need to
be duplicated.

When dealing with credentials, take steps to maintain the security
of the credentials.  For instance, if transporting credentials over
a network, be sure to encrypt the credentials.


GINASTUB.H

//
// function prototypes for the gina interface
//

typedef BOOL (WINAPI *PGWLXNEGOTIATE)( DWORD, DWORD* );
typedef BOOL (WINAPI *PGWLXINITIALIZE)( LPWSTR, HANDLE, PVOID, PVOID, PVOID* );
typedef VOID (WINAPI *PGWLXDISPLAYSASNOTICE)( PVOID );
typedef int  (WINAPI *PGWLXLOGGEDOUTSAS)( PVOID, DWORD, PLUID, PSID, PDWORD,
                                        PHANDLE, PWLX_MPR_NOTIFY_INFO, PVOID *);
typedef BOOL (WINAPI *PGWLXACTIVATEUSERSHELL)(  PVOID, PWSTR, PWSTR, PVOID );
typedef int  (WINAPI *PGWLXLOGGEDONSAS)( PVOID, DWORD, PVOID );
typedef VOID (WINAPI *PGWLXDISPLAYLOCKEDNOTICE)( PVOID );
typedef int  (WINAPI *PGWLXWKSTALOCKEDSAS)( PVOID, DWORD );
typedef BOOL (WINAPI *PGWLXISLOCKOK)( PVOID );
typedef BOOL (WINAPI *PGWLXISLOGOFFOK)( PVOID );
typedef VOID (WINAPI *PGWLXLOGOFF)( PVOID );
typedef VOID (WINAPI *PGWLXSHUTDOWN)( PVOID, DWORD );

//
// NEW for version 1.1
//
typedef BOOL (WINAPI *PGWLXSCREENSAVERNOTIFY)( PVOID, BOOL * );
typedef BOOL (WINAPI *PGWLXSTARTAPPLICATION)( PVOID, PWSTR, PVOID, PWSTR );


GINASTUB.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    ginastub.c

Abstract:

    This sample illustrates a pass-thru "stub" gina which can be used
    in some cases to simplify gina development.

    A common use for a gina is to implement code which requires the
    credentials of the user logging onto the workstation.  The credentials
    may be required for syncronization with foreign account databases
    or custom authentication activities.

    In this example case, it is possible to implement a simple gina
    stub layer which simply passes control for the required functions
    to the previously installed gina, and captures the interesting
    parameters from that gina.  In this scenario, the existing functionality
    in the existent gina is retained.  In addition, the development time
    is reduced drastically, as existing functionality does not need to
    be duplicated.

    When dealing with credentials, take steps to maintain the security
    of the credentials.  For instance, if transporting credentials over
    a network, be sure to encrypt the credentials.

Author:

    Scott Field (sfield)    18-Jul-96

--*/

#include <windows.h>
#include <stdio.h>
#include <winwlx.h>

#include "ginastub.h"

//
// Location of the real msgina.
//

#define REALGINA_PATH   TEXT("MSGINA.DLL")

//
// winlogon function dispatch table
//

PWLX_DISPATCH_VERSION_1_0 g_pWinlogon;

//
// Functions pointers to the real msgina which we will call.
//

PGWLXNEGOTIATE GWlxNegotiate;
PGWLXINITIALIZE GWlxInitialize;
PGWLXDISPLAYSASNOTICE GWlxDisplaySASNotice;
PGWLXLOGGEDOUTSAS GWlxLoggedOutSAS;
PGWLXACTIVATEUSERSHELL GWlxActivateUserShell;
PGWLXLOGGEDONSAS GWlxLoggedOnSAS;
PGWLXDISPLAYLOCKEDNOTICE GWlxDisplayLockedNotice;
PGWLXWKSTALOCKEDSAS GWlxWkstaLockedSAS;
PGWLXISLOCKOK GWlxIsLockOk;
PGWLXISLOGOFFOK GWlxIsLogoffOk;
PGWLXLOGOFF GWlxLogoff;
PGWLXSHUTDOWN GWlxShutdown;

//
// NEW for version 1.1
//

PGWLXSTARTAPPLICATION GWlxStartApplication;
PGWLXSCREENSAVERNOTIFY GWlxScreenSaverNotify;

//
// hook into the real GINA.
//

BOOL
MyInitialize( void )
{
    HINSTANCE hDll;

    //
    // Load MSGINA.DLL.
    //
    if( !(hDll = LoadLibrary( REALGINA_PATH )) ) {
        return FALSE;
    }

    //
    // Get pointers to all of the WLX functions in the real MSGINA.
    //
    GWlxNegotiate = (PGWLXNEGOTIATE)GetProcAddress( hDll, "WlxNegotiate" );
    if( !GWlxNegotiate ) {
        return FALSE;
    }

    GWlxInitialize = (PGWLXINITIALIZE)GetProcAddress( hDll, "WlxInitialize" );
    if( !GWlxInitialize ) {
        return FALSE;
    }

    GWlxDisplaySASNotice =
        (PGWLXDISPLAYSASNOTICE)GetProcAddress( hDll, "WlxDisplaySASNotice" );
    if( !GWlxDisplaySASNotice ) {
        return FALSE;
    }

    GWlxLoggedOutSAS =
        (PGWLXLOGGEDOUTSAS)GetProcAddress( hDll, "WlxLoggedOutSAS" );
    if( !GWlxLoggedOutSAS ) {
        return FALSE;
    }

    GWlxActivateUserShell =
        (PGWLXACTIVATEUSERSHELL)GetProcAddress( hDll, "WlxActivateUserShell" );
    if( !GWlxActivateUserShell ) {
        return FALSE;
    }

    GWlxLoggedOnSAS =
        (PGWLXLOGGEDONSAS)GetProcAddress( hDll, "WlxLoggedOnSAS" );
    if( !GWlxLoggedOnSAS ) {
        return FALSE;
    }

    GWlxDisplayLockedNotice =
        (PGWLXDISPLAYLOCKEDNOTICE)GetProcAddress(
                                        hDll,
                                        "WlxDisplayLockedNotice" );
    if( !GWlxDisplayLockedNotice ) {
        return FALSE;
    }

    GWlxIsLockOk = (PGWLXISLOCKOK)GetProcAddress( hDll, "WlxIsLockOk" );
    if( !GWlxIsLockOk ) {
        return FALSE;
    }

    GWlxWkstaLockedSAS =
        (PGWLXWKSTALOCKEDSAS)GetProcAddress( hDll, "WlxWkstaLockedSAS" );
    if( !GWlxWkstaLockedSAS ) {
        return FALSE;
    }

    GWlxIsLogoffOk = (PGWLXISLOGOFFOK)GetProcAddress( hDll, "WlxIsLogoffOk" );
    if( !GWlxIsLogoffOk ) {
        return FALSE;
    }

    GWlxLogoff = (PGWLXLOGOFF)GetProcAddress( hDll, "WlxLogoff" );
    if( !GWlxLogoff ) {
        return FALSE;
    }

    GWlxShutdown = (PGWLXSHUTDOWN)GetProcAddress( hDll, "WlxShutdown" );
    if( !GWlxShutdown ) {
        return FALSE;
    }

    //
    // we don't check for failure here because these don't exist for
    // gina's implemented prior to Windows NT 4.0
    //

    GWlxStartApplication = (PGWLXSTARTAPPLICATION) GetProcAddress( hDll, "WlxStartApplication" );
    GWlxScreenSaverNotify = (PGWLXSCREENSAVERNOTIFY) GetProcAddress( hDll, "WlxScreenSaverNotify" );

    //
    // Everything loaded ok.  Return success.
    //
    return TRUE;
}

BOOL
WINAPI
WlxNegotiate(
    DWORD       dwWinlogonVersion,
    DWORD       *pdwDllVersion)
{
    if( !MyInitialize() )
        return FALSE;

    return GWlxNegotiate( dwWinlogonVersion, pdwDllVersion );
}

BOOL
WINAPI
WlxInitialize(
    LPWSTR      lpWinsta,
    HANDLE      hWlx,
    PVOID       pvReserved,
    PVOID       pWinlogonFunctions,
    PVOID       *pWlxContext)
{
    return GWlxInitialize(
                lpWinsta,
                hWlx,
                pvReserved,
                pWinlogonFunctions,
                pWlxContext
                );
}

VOID
WINAPI
WlxDisplaySASNotice(
    PVOID   pWlxContext)
{
    GWlxDisplaySASNotice( pWlxContext );
}

int
WINAPI
WlxLoggedOutSAS(
    PVOID           pWlxContext,
    DWORD           dwSasType,
    PLUID           pAuthenticationId,
    PSID            pLogonSid,
    PDWORD          pdwOptions,
    PHANDLE         phToken,
    PWLX_MPR_NOTIFY_INFO    pMprNotifyInfo,
    PVOID           *pProfile)
{
    int iRet;

    iRet = GWlxLoggedOutSAS(
                pWlxContext,
                dwSasType,
                pAuthenticationId,
                pLogonSid,
                pdwOptions,
                phToken,
                pMprNotifyInfo,
                pProfile
                );

    if(iRet == WLX_SAS_ACTION_LOGON) {
        //
        // copy pMprNotifyInfo and pLogonSid for later use
        //

        // pMprNotifyInfo->pszUserName
        // pMprNotifyInfo->pszDomain
        // pMprNotifyInfo->pszPassword
        // pMprNotifyInfo->pszOldPassword

    }

    return iRet;
}

BOOL
WINAPI
WlxActivateUserShell(
    PVOID           pWlxContext,
    PWSTR           pszDesktopName,
    PWSTR           pszMprLogonScript,
    PVOID           pEnvironment)
{
    return GWlxActivateUserShell(
                pWlxContext,
                pszDesktopName,
                pszMprLogonScript,
                pEnvironment
                );
}

int
WINAPI
WlxLoggedOnSAS(
    PVOID           pWlxContext,
    DWORD           dwSasType,
    PVOID           pReserved)
{
    return GWlxLoggedOnSAS( pWlxContext, dwSasType, pReserved );
}

VOID
WINAPI
WlxDisplayLockedNotice(
    PVOID           pWlxContext )
{
    GWlxDisplayLockedNotice( pWlxContext );
}

BOOL
WINAPI
WlxIsLockOk(
    PVOID           pWlxContext)
{
    return GWlxIsLockOk( pWlxContext );
}

int
WINAPI
WlxWkstaLockedSAS(
    PVOID           pWlxContext,
    DWORD           dwSasType )
{
    return GWlxWkstaLockedSAS( pWlxContext, dwSasType );
}

BOOL
WINAPI
WlxIsLogoffOk(
    PVOID pWlxContext
    )
{
    BOOL bSuccess;

    bSuccess = GWlxIsLogoffOk( pWlxContext );

    if(bSuccess) {

        //
        // if it's ok to logoff, finish with the stored credentials
        // and scrub the buffers
        //

    }

    return bSuccess;
}

VOID
WINAPI
WlxLogoff(
    PVOID pWlxContext
    )
{
    GWlxLogoff( pWlxContext );
}

VOID
WINAPI
WlxShutdown(
    PVOID pWlxContext,
    DWORD ShutdownType
    )
{
    GWlxShutdown( pWlxContext, ShutdownType );
}

//
// NEW for version 1.1
//

BOOL
WINAPI
WlxScreenSaverNotify(
    PVOID                   pWlxContext,
    BOOL *                  pSecure
    )
{
    if(GWlxScreenSaverNotify != NULL)
        return GWlxScreenSaverNotify( pWlxContext, pSecure );

    //
    // if not exported, return something intelligent
    //

    *pSecure = TRUE;

    return TRUE;
}

BOOL
WINAPI
WlxStartApplication(
    PVOID                   pWlxContext,
    PWSTR                   pszDesktopName,
    PVOID                   pEnvironment,
    PWSTR                   pszCmdLine
    )
{
    if(GWlxStartApplication != NULL)
        return GWlxStartApplication(
            pWlxContext,
            pszDesktopName,
            pEnvironment,
            pszCmdLine
            );

    //
    // if not exported, return something intelligent
    //

}


README.TXT

HTTPAuth

The elements described here are not a part of the Win32 API. They are
specific to Security Providers and Account Management.

Please be aware In the future many of these elements will be replaced
with OLE Interfaces, and will subsequently be dropped.

Documentation on sspi.h can be found in \mstools\help\spk.mvb --
use \mstools\help\infoview.exe spk.mvb to view.


GET_SOCK.C

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    get_sock.c

Abstract:

    This does a get using raw sockets

History:

    09-Nov-1995     Created
    15-Feb-1996     Added authentication support

--*/

#include <windows.h>
#include <winsock.h>

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "httpauth.h"

// globals

#define HD_AUTHENTICATE         "WWW-Authenticate:"
#define HD_LENGTH               "Content-Length:"
#define HD_CONNECTION           "Connection:"

// list of Authentication methods to support

char      achAuth[256];

// Helper functions

void SkipWhite(PSTR *ppS )
{
    PSTR pS = *ppS;

    while ( *pS && isspace(*pS) )
        ++pS;

    *ppS = pS;
}

void SkipNonWhite(PSTR *ppS )
{
    PSTR pS = *ppS;

    while ( *pS && !isspace(*pS) )
        ++pS;

    *ppS = pS;
}

BOOL
HttpGetSocket(
    char * Verb,
    char * Server,
    char * URL,
    BOOL   DisplayHeaders,
    DWORD  ClientDataSize,
    PSTR   pchUserName,
    PSTR   pchPassword,
    PSTR   pszStore,
    PSTR   pszPref
    )
/*++

 Routine Description:

    Issue a command to a HTTP server using authentication

 Arguments:

    Verb                HTTP command : GET / HEAD / ...
    Server              server name
    URL                 URL to send to the server
    DisplayHeaders      TRUE to display headers as received
    ClientDataSize      number of bytes to send in the request
    pchUserName         user name for authentication
    pchPassword         password for authentication
    pszStore            file name where to dump reply from server

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
    char               ReceiveBuffer[8*1024];
    int                Error;
    BYTE               Request[1024];
    int                RequestSize;
    char *             AcceptTypes[2] = {"*/*", NULL};
    SOCKET             Socket = INVALID_SOCKET;
    WSADATA            WsaData;
    struct sockaddr_in Address;
    struct hostent *   HostEntry;
    char               Headers[] =
                           "HTTP/1.0\r\n"
                           "User-Agent: AuthClient\r\n"
                           "Accept: */*\r\n";
    char               CrLf[] = "\r\n";
    BYTE               ClientData[64*1024];
    BOOL               fKeepAlive = FALSE;
    int                cRec;
    DWORD              cLen;
    BOOL               fInHeader;
    PSTR               pchAuthData;
    BOOL               fServerKeepAlive = FALSE;
    BOOL               fNeedMoreData;
    BOOL               fNeedAuthenticate;
    PSTR               pH;
    PSTR               pN;
    BOOL               fStatusLine;
    int                Status = -1;
    DWORD              cToRead;
    PSTR               paAuth = achAuth;
    BOOL               fSt = FALSE;
    int                hnd = EOF;

    Error = WSAStartup (0x101, &WsaData);

    if (Error == SOCKET_ERROR)
    {
        fprintf(stderr, "Error in WSAStartup = %d\n", GetLastError());
        return FALSE;
    }

    if ( !InitAuthorizationHeader() )
    {
        fprintf(stderr, "Cannot initialize security module\n" );
        return FALSE;
    }

    memset( achAuth, '\0', sizeof(achAuth) );
    pchAuthData = NULL;
    fNeedAuthenticate = FALSE;

    //
    // Connect to the server
    //

    if  ( pszStore != NULL )
        if ( (hnd = _open( pszStore, _O_BINARY | _O_CREAT | _O_TRUNC | _O_RDWR, S_IREAD|S_IWRITE )) == EOF )
        {
            fprintf( stderr, "Can't create file %s\n", pszStore );
            return FALSE;
        }

again:

    if ( Socket == INVALID_SOCKET )
    {
        Socket = socket(AF_INET, SOCK_STREAM, 0);

        if (Socket == INVALID_SOCKET)
        {
            fprintf(stderr, "Error creating socket = %d\n", GetLastError());
            fSt = FALSE;
            goto ex;
        }

        Address.sin_family = AF_INET;
        Address.sin_port = 0;
        Address.sin_addr.s_addr = INADDR_ANY;

        Error =
        bind(
            Socket,
            (struct sockaddr *) &Address,
            sizeof(Address));

        if (Error)
        {
            fprintf(stderr, "Error in bind = %d\n", GetLastError());
            fSt = FALSE;
            goto ex;
        }

        Address.sin_family = AF_INET;
        Address.sin_port = htons(80);
        Address.sin_addr.s_addr = inet_addr(Server);

        if (Address.sin_addr.s_addr == -1)
        {
            //
            // Must be a server name
            //
            HostEntry = gethostbyname(Server);
            if (HostEntry == NULL)
            {
                printf("unable to resolve %s\n", Server);
                fSt = FALSE;
                goto ex;
            } else
            {
                Address.sin_addr.s_addr = *((unsigned long *) HostEntry->h_addr);
            }
        }

        Error =
        connect(
            Socket,
            (struct sockaddr *) &Address,
            sizeof(Address));

        if (Error)
        {
            fprintf(stderr, "Error connecting to %s = %d\n", Server, GetLastError());
            fSt = FALSE;
            goto ex;
        }
    }

    //
    // Send the client request
    //

    strcpy(Request, Verb);
    strcat(Request, " ");
    strcat(Request, URL);
    strcat(Request, " ");
    strcat(Request, Headers);
    if (ClientDataSize)
    {
        sprintf(ClientData, "Content-Length: %d\r\n", ClientDataSize);
        strcat(Request, ClientData);
    }
    if ( fKeepAlive )
    {
        strcat(Request, "Connection: Keep-Alive\r\n" );
    }

    if ( !AddAuthorizationHeader( Request + strlen(Request), achAuth, pchAuthData, pchUserName, pchPassword, &fNeedMoreData ) )
    {
        printf( "Authentication failed\n" );
        fSt = FALSE;
        goto ex;
    }

    strcat(Request, CrLf);

    RequestSize = strlen(Request);

    Error =
    send(
        Socket,
        Request,
        RequestSize,
        0);

    if (Error != RequestSize)
    {
        printf("Error in client send = %d, %d\n", Error, GetLastError());
        fSt = FALSE;
        goto ex;
    }

    if (ClientDataSize)
    {
        memset( ClientData, ' ', ClientDataSize );

        //
        // Send the client data
        //

        Error =
        send(
            Socket,
            ClientData,
            ClientDataSize,
            0);

        if ( (DWORD)Error != ClientDataSize)
        {
            printf("Error in client send = %d, %d\n", Error, GetLastError());
            fSt = FALSE;
            goto ex;
        }
    }

    // parse status & header

    cLen = (DWORD)-1;
    fInHeader = TRUE;
    fServerKeepAlive = FALSE;
    fNeedAuthenticate = FALSE;

    for ( pH = ReceiveBuffer, fStatusLine = TRUE ; fInHeader ; )
    {
        cRec = recv( Socket, pH, ReceiveBuffer+sizeof(ReceiveBuffer)-pH, 0 );
        if ( cRec <= 0 )
        {
            closesocket( Socket );
            Socket = INVALID_SOCKET;
            break;
        }
        pH[ cRec ] = '\0';

        // Iterate on header fields

        while ( pN = strstr(pH, "\r\n" ) )
        {
            *pN = '\0';

            if ( DisplayHeaders )
            {
                printf( "%s\n", pH );
            }

            if ( fStatusLine )
            {
                // This is the status line, decode status

                SkipNonWhite( &pH );
                SkipWhite( &pH );
                Status = atoi( pH );
                if ( Status == 401 )
                {
                    fNeedAuthenticate = TRUE;
                }
                fStatusLine = FALSE;
            }
            else if ( pN == pH )    // end of header fields
            {
                if ( hnd != EOF )
                    write( hnd, pH+2, ReceiveBuffer+cRec-pH-2 );

                cLen -= ( ReceiveBuffer+cRec-pH-2 );
                fInHeader = FALSE;
                break;
            }
            else if ( !strnicmp( pH, HD_AUTHENTICATE, sizeof(HD_AUTHENTICATE)-1 ) )
            {
                SkipNonWhite( &pH );
                SkipWhite( &pH );

                // check if we are already in the authentication sequence

                if ( !IsInAuthorizationSequence() )
                {
                    // append to list of supported authentication methods

                    strcpy( paAuth, pH );
                    paAuth += strlen( pH ) + 1;
                }
                else
                {
                    // store pointer to authenticate blob

                    SkipNonWhite( &pH );
                    SkipWhite( &pH );
                    pchAuthData = pH;
                }
            }
            else if ( !strnicmp( pH, HD_LENGTH, sizeof(HD_LENGTH)-1 ) )
            {
                // get content length

                SkipNonWhite( &pH );
                SkipWhite( &pH );
                cLen = atoi( pH );
            }
            else if ( !strnicmp( pH, HD_CONNECTION, sizeof(HD_CONNECTION)-1 ) )
            {
                // check for keep-alive flag

                SkipNonWhite( &pH );
                SkipWhite( &pH );
                if ( !strnicmp( pH, "Keep-Alive", sizeof("Keep-Alive")-1 ) )
                    fServerKeepAlive = TRUE;
            }

            pH = pN + 2;
        }
    }

    // add final delimiter to list of supported authentication methods

    if ( !IsInAuthorizationSequence() && fNeedAuthenticate )
    {
        *paAuth = '\0';

        // Insure specified methods are supported localy

        if ( !ValidateAuthenticationMethods( achAuth, pszPref ) )
        {
            // None of the server specified authentication methods
            // are supported localy.

            SetLastError( ERROR_ACCESS_DENIED );

            fprintf( stderr, "No supported authentication method\n" );

            fSt = FALSE;
            goto ex;
        }
    }

    // read message body

    if ( Socket != INVALID_SOCKET )
    {
        for ( ; cLen ; )
        {
            if ( (cToRead = sizeof(ReceiveBuffer)) > cLen )
                cToRead = cLen;
            cRec = recv( Socket, ReceiveBuffer, cToRead, 0 );

            if ( cRec <= 0 )
            {
                closesocket( Socket );
                Socket = INVALID_SOCKET;
                break;
            }
            if ( hnd != EOF )
                write( hnd, ReceiveBuffer, cRec );

            cLen -= cRec;
        }
    }

    if ( !fServerKeepAlive )
    {
        if ( IsInAuthorizationSequence() )
        {
            fprintf( stderr, "Authentication rejected by server\n" );

            fNeedAuthenticate = FALSE;  // authentication failed
        }

        closesocket( Socket );
        Socket = INVALID_SOCKET;
    }

    if ( fNeedAuthenticate )
    {
        fKeepAlive = TRUE;
        goto again;
    }

    if ( Socket != INVALID_SOCKET )
        closesocket(Socket);

    fSt = Status == 200;

ex:
    TerminateAuthorizationHeader();

    if ( hnd != EOF )
        close( hnd );

    return fSt;
}


HTTPAUTH.C

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    httpauth.c

Abstract:

    Handles authentication sequence ( Basic & SSPI )

History:

    Created     15-Feb-1996

Revision History:

--*/

/************************************************************
 *    Include Headers
 ************************************************************/

#include <windows.h>
#include <rpc.h>
#include <winsock.h>
#include <lm.h>

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <fcntl.h>

#define SECURITY_WIN32
#include "sspi.h"
#include "issperr.h"

// declaration for this module

#include "httpauth.h"

#define SEC_SUCCESS(Status) ((Status) >= 0)
#define STATUS_SUCCESS 0

// Target name for the security package

#define TOKEN_SOURCE_NAME       "InetSvcs"

// general purpose dynamic buffer structure

typedef struct _BUFFER {
    PBYTE pBuf;
    DWORD cLen;
} BUFFER ;

// structure storing the state of the authentication sequence

typedef struct _AUTH_SEQ {
    BOOL _fNewConversation;
    CredHandle _hcred;
    BOOL _fHaveCredHandle;
    DWORD _cbMaxToken;
    BOOL _fHaveCtxtHandle;
    struct _SecHandle  _hctxt;
    BOOL _fUUEncodeData;
} AUTH_SEQ;

// entry points in the security DLL

typedef struct _SEC_FUNC {
    FREE_CREDENTIALS_HANDLE_FN pFreeCredentialsHandle;
    ACQUIRE_CREDENTIALS_HANDLE_FN pAcquireCredentialsHandle;
    QUERY_SECURITY_PACKAGE_INFO_FN pQuerySecurityPackageInfo;   // A
    FREE_CONTEXT_BUFFER_FN pFreeContextBuffer;
    INITIALIZE_SECURITY_CONTEXT_FN pInitializeSecurityContext;  // A
    COMPLETE_AUTH_TOKEN_FN pCompleteAuthToken;
    ENUMERATE_SECURITY_PACKAGES_FN pEnumerateSecurityPackages;  // A
} SEC_FUNC;

// local functions

BOOL CrackUserAndDomain(
    CHAR *   pszDomainAndUser,
    CHAR * * ppszUser,
    CHAR * * ppszDomain
    );

BOOL AuthConverse(
    AUTH_SEQ *pAS,
    VOID   * pBuffIn,
    DWORD    cbBuffIn,
    BUFFER * pbuffOut,
    DWORD  * pcbBuffOut,
    BOOL   * pfNeedMoreData,
    CHAR   * pszPackage,
    CHAR   * pszUser,
    CHAR   * pszPassword
    );

BOOL AuthInit( AUTH_SEQ *pAS );

void AuthTerminate( AUTH_SEQ *pAS );

// uuencode/decode routines declaration
// used to code the authentication blob

BOOL uudecode(char   * bufcoded,
              BUFFER * pbuffdecoded,
              DWORD  * pcbDecoded );
BOOL uuencode( BYTE *   bufin,
               DWORD    nbytes,
               BUFFER * pbuffEncoded );

/************************************************************
 *    Globals for this module
 ************************************************************/

static BOOL g_fAuth = FALSE;
static BOOL g_fBasic = FALSE;
static AUTH_SEQ g_Auth;
static HINSTANCE g_hSecLib = NULL;
static SEC_FUNC sfProcs;

/************************************************************
 *    Helper functions
 ************************************************************/

BOOL BufferInit( BUFFER *pB )
{
    pB->pBuf = NULL;
    pB->cLen = 0;

    return TRUE;
}

void BufferTerminate( BUFFER *pB )
{
    if ( pB->pBuf != NULL )
    {
        free( pB->pBuf );
        pB->pBuf = NULL;
        pB->cLen = 0;
    }
}

PBYTE BufferQueryPtr( BUFFER * pB )
{
    return pB->pBuf;
}

BOOL BufferResize( BUFFER *pB, DWORD cNewL )
{
    PBYTE pN;
    if ( cNewL > pB->cLen )
    {
        pN = malloc( cNewL );
        if ( pB->pBuf )
        {
            memcpy( pN, pB->pBuf, pB->cLen );
            free( pB->pBuf );
        }
        pB->pBuf = pN;
        pB->cLen = cNewL;
    }

    return TRUE;
}

/************************************************************
 *    Authentication functions
 ************************************************************/

BOOL
InitAuthorizationHeader(
    )
/*++

 Routine Description:

    Initialize the authentication package

 Arguments:

    None

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
    OSVERSIONINFO   VerInfo;
    UCHAR lpszDLL[MAX_PATH];

    //
    //  Find out which security DLL to use, depending on
    //  whether we are on NT or Win95
    //
    VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
    if (!GetVersionEx (&VerInfo))   // If this fails, something has gone wrong
    {
        return FALSE;
    }

    if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
    {
        strcpy (lpszDLL, "security.dll" );
    }
    else if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
    {
        strcpy (lpszDLL, "secur32.dll" );
    }
    else
    {
        return FALSE;
    }

    //
    //  Load Security DLL
    //

    g_hSecLib = LoadLibrary (lpszDLL);
    if (g_hSecLib == NULL)
    {
        return FALSE;
    }

    // Get all entry points we care about

    sfProcs.pFreeCredentialsHandle
            = (FREE_CREDENTIALS_HANDLE_FN) GetProcAddress(
                    g_hSecLib,
                    "FreeCredentialsHandle" );

    sfProcs.pQuerySecurityPackageInfo
            = (QUERY_SECURITY_PACKAGE_INFO_FN) GetProcAddress(
                    g_hSecLib,
                    "QuerySecurityPackageInfoA" );

    sfProcs.pAcquireCredentialsHandle
            = (ACQUIRE_CREDENTIALS_HANDLE_FN) GetProcAddress(
                    g_hSecLib,
                    "AcquireCredentialsHandleA" );

    sfProcs.pFreeContextBuffer
            = (FREE_CONTEXT_BUFFER_FN) GetProcAddress(
                    g_hSecLib,
                    "FreeContextBuffer" );

    sfProcs.pInitializeSecurityContext
            = (INITIALIZE_SECURITY_CONTEXT_FN) GetProcAddress(
                    g_hSecLib,
                    "InitializeSecurityContextA" );

    sfProcs.pCompleteAuthToken
            = (COMPLETE_AUTH_TOKEN_FN) GetProcAddress(
                    g_hSecLib,
                    "CompleteAuthToken" );

    sfProcs.pEnumerateSecurityPackages
            = (ENUMERATE_SECURITY_PACKAGES_FN) GetProcAddress(
                    g_hSecLib,
                    "EnumerateSecurityPackagesA" );

    if ( sfProcs.pFreeCredentialsHandle == NULL
            || sfProcs.pQuerySecurityPackageInfo == NULL
            || sfProcs.pAcquireCredentialsHandle == NULL
            || sfProcs.pFreeContextBuffer == NULL
            || sfProcs.pInitializeSecurityContext == NULL
            || sfProcs.pEnumerateSecurityPackages == NULL )
    {
        FreeLibrary( g_hSecLib );
        g_hSecLib = NULL;
        return FALSE;
    }

    g_fAuth = g_fBasic = FALSE;

    return TRUE;
}

void
TerminateAuthorizationHeader(
    )
/*++

 Routine Description:

    Terminate the authentication package

 Arguments:

    None

 Return Value:

    None

--*/
{
    if ( g_fAuth )
    {
        AuthTerminate( &g_Auth );
        g_fAuth = FALSE;
    }

    g_fBasic = FALSE;

    if ( g_hSecLib != NULL )
    {
        FreeLibrary( g_hSecLib );
        g_hSecLib = NULL;
    }
}

BOOL
IsInAuthorizationSequence(
    )
/*++

 Routine Description:

    Indicates if in authentication sequence

 Arguments:

    None

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
    return g_fAuth || g_fBasic;
}

BOOL
ValidateAuthenticationMethods(
    PSTR pszMet,
    PSTR pszPreferedMet )
/*++

 Routine Description:

    Filter the supplied authentication list for supported
    methods ( Basic and all local security packages )

 Arguments:

    None

 Return Value:

    Returns TRUE if at least one authentication method supported,
    otherwise FALSE is returned.

--*/
{
    PSecPkgInfo pSec;
    ULONG cSec, iS;
    BOOL fValid;
    PSTR p,t;
    SECURITY_STATUS ss;

    // access local security packages list

    if ( (ss = sfProcs.pEnumerateSecurityPackages( &cSec, &pSec ))
            == STATUS_SUCCESS )
    {
        for ( t = p = pszMet ; *p ; )
        {
            // Valid methods are "Basic" and all security packages

            if ( !_stricmp( p , "Basic" ) )
                fValid = TRUE;
            else for ( iS = 0 ; iS < cSec ; ++iS )
                if ( !_stricmp( pSec[iS].Name, p ) )
                    break;

            if ( fValid )
            {
                if ( t != p )
                    memmove( t, p, strlen(p)+1 );
                p += strlen( p ) + 1;
                t += strlen( t ) + 1;
            }
        }
        *t = '\0';
    }

    // check for prefered method

    if ( pszPreferedMet != NULL )
    {
        PSTR pP;

        for ( pP = strtok( pszPreferedMet, "," ) ;
                pP != NULL ;
                pP = strtok( NULL, "," ) )
        {
            // scan list of validated methods for the current
            // prefered method

            for ( p = pszMet ; *p ; )
            {
                if ( !_stricmp( pP, p ) )
                {
                    memmove( pszMet, p, strlen(p) + 1 );
                    return TRUE;
                }
                p += strlen( p ) + 1;
            }
        }

        // no method in the prefered method list is supported

        return FALSE;
    }

    // no prefered method list supplied

    return *pszMet ? TRUE : FALSE;
}

BOOL
AddAuthorizationHeader(
    PSTR pch,
    PSTR pchSchemes,
    PSTR pchAuthData,
    PSTR pchUserName,
    PSTR pchPassword,
    BOOL *pfNeedMoreData
    )
/*++

 Routine Description:

    Generates an authentication header to be sent to the server.
    An authentication sequence will be initiated if none is in
    use and at least one of the authentication scheme specified
    in pchSchemes is recognized.
    Otherwise the current authentication sequence will proceed.

 Arguments:

    pch                 where to append authentication header
    pchSchemes          list of null-delimited authentication methods
    pchAuthData         incoming blob from server
    pchUserName         user name ( possibly prefixed with domain )
    pchPassword         password
    pfNeedMoreData      Out: TRUE if authentication sequence to continue

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
    CHAR   achUserAndPass[256];
    BUFFER buff;
    BOOL fSt = TRUE;
    DWORD cbOut;

    BufferInit( &buff );

    while ( *pchSchemes )
    {
        if ( !stricmp( pchSchemes, "Basic" ))
        {
            // if already in authentication sequence, it failed.

            if ( g_fBasic )
            {
                SetLastError( ERROR_ACCESS_DENIED );
                fSt = FALSE;
                break;
            }

            strcpy( achUserAndPass, pchUserName );
            strcat( achUserAndPass, ":" );
            strcat( achUserAndPass, pchPassword );

            uuencode( (BYTE *) achUserAndPass,
                      strlen( achUserAndPass ),
                      &buff );
            strcat( pch, "Authorization: " );
            strcat( pch, "Basic " );
            strcat( pch, BufferQueryPtr( &buff ));
            strcat( pch, "\r\n");
            g_fBasic = TRUE;
            break;
        }
        else
        {
            // SSPI package ( assuming methods list have been validated )

            if ( !g_fAuth )
            {
                if ( !AuthInit( &g_Auth ) )
                {
                    fSt = FALSE;
                    goto ex;
                }
            }
            else if ( pchAuthData == NULL || *pchAuthData == '\0' )
            {
                // no blob while in authentication sequence : it failed

                SetLastError( ERROR_ACCESS_DENIED );
                fSt = FALSE;
                break;
            }

            if ( !AuthConverse( &g_Auth,
                  (void *) pchAuthData,
                  0,
                  &buff,
                  &cbOut,
                  pfNeedMoreData,
                  pchSchemes,
                  pchUserName,
                  pchPassword ))
            {
                fSt = FALSE;
                goto ex;
            }

            strcat( pch, "Authorization: " );
            strcat( pch, pchSchemes );
            strcat( pch, " " );
            strcat( pch, (CHAR *) BufferQueryPtr( &buff ) );
            strcat( pch, "\r\n" );
            break;
        }

        pchSchemes += strlen(pchSchemes) + 1;
    }
ex:
    BufferTerminate( &buff );

    return fSt;
}

BOOL
AuthInit(
    AUTH_SEQ *pAS )
/*++

 Routine Description:

    Initialize a SSP authentication sequence

 Arguments:

    None

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
    pAS->_fNewConversation = TRUE;
    pAS->_fHaveCredHandle = FALSE;
    pAS->_fHaveCtxtHandle = FALSE;
    pAS->_fUUEncodeData = TRUE;

    g_fAuth = TRUE;

    return TRUE;
}

void
AuthTerminate(
    AUTH_SEQ *pAS )
/*++

 Routine Description:

    Terminate a SSP authentication sequence

 Arguments:

    None

 Return Value:

    None

--*/
{
    if ( pAS->_fHaveCredHandle )
        sfProcs.pFreeCredentialsHandle( &pAS->_hcred );

    pAS->_fHaveCredHandle = FALSE;
    pAS->_fHaveCtxtHandle = FALSE;
}

BOOL
AuthConverse(
    AUTH_SEQ *pAS,
    VOID   * pBuffIn,
    DWORD    cbBuffIn,
    BUFFER * pbuffOut,
    DWORD  * pcbBuffOut,
    BOOL   * pfNeedMoreData,
    CHAR   * pszPackage,
    CHAR   * pszUser,
    CHAR   * pszPassword
    )
/*++

Routine Description:

    Initiates or continues a previously initiated authentication conversation

    Client calls this first to get the negotiation message which
    it then sends to the server.  The server calls this with the
    client result and sends back the result.  The conversation
    continues until *pfNeedMoreData is FALSE.

    On the first call, pszPackage must point to the zero terminated
    authentication package name to be used and pszUser and pszPassword
    should point to the user name and password to authenticated with
    on the client side (server side will always be NULL).

Arguments:

    pBuffIn - Points to SSP message received from the
        client.  If UUENCODE is used, then this must point to a
        zero terminated uuencoded string (except for the first call).
    cbBuffIn - Number of bytes in pBuffIn or zero if pBuffIn points to a
        zero terminated, uuencoded string.
    pbuffOut - If *pfDone is not set to TRUE, this buffer contains the data
        that should be sent to the other side.  If this is zero, then no
        data needs to be sent.
    pcbBuffOut - Number of bytes in pbuffOut
    pfNeedMoreData - Set to TRUE while this side of the conversation is
        expecting more data from the remote side.
    pszPackage - On the first call points to a zero terminate string indicating
        the security package to use
    pszUser - Specifies user or domain\user the first time the client calls
        this method (client side only)
    pszPassword - Specifies the password for pszUser the first time the
        client calls this method (client side only)

Return Value:

    TRUE if successful, FALSE otherwise (call GetLastError).  Access is
    denied if FALSE is returned and GetLastError is ERROR_ACCESS_DENIED.

--*/
{
    SECURITY_STATUS       ss;
    TimeStamp             Lifetime;
    SecBufferDesc         OutBuffDesc;
    SecBuffer             OutSecBuff;
    SecBufferDesc         InBuffDesc;
    SecBuffer             InSecBuff;
    ULONG                 ContextAttributes;
    BUFFER                buffData;
    BUFFER                buff;
    BOOL                  fSt;
    BOOL                  fReply;

    BufferInit( &buffData );
    BufferInit( &buff );

    //
    //  Decode the data if there's something to decode
    //

    if ( pAS->_fUUEncodeData && pBuffIn )
    {
        if ( !uudecode( (CHAR *) pBuffIn,
                        &buffData,
                        &cbBuffIn ))
        {
            fSt = FALSE;
            goto ex;
        }

        pBuffIn = BufferQueryPtr( &buffData );
    }

    //
    //  If this is a new conversation, then we need to get the credential
    //  handle and find out the maximum token size
    //

    if ( pAS->_fNewConversation )
    {
        SecPkgInfo *              pspkg;
        SEC_WINNT_AUTH_IDENTITY   AuthIdentity;
        SEC_WINNT_AUTH_IDENTITY * pAuthIdentity;
        CHAR *                    pszDomain = NULL;
        CHAR                      szDomainAndUser[DNLEN+UNLEN+2];

        //
        //  fill out the authentication information
        //

        if ( ((pszUser != NULL) ||
              (pszPassword != NULL)) )
        {
            pAuthIdentity = &AuthIdentity;

            //
            //  Break out the domain from the username if one was specified
            //

            if ( pszUser != NULL )
            {
                strcpy( szDomainAndUser, pszUser );
                if ( !CrackUserAndDomain( szDomainAndUser,
                                          &pszUser,
                                          &pszDomain ))
                {
                    return FALSE;
                }
            }

            memset( &AuthIdentity,
                    0,
                    sizeof( AuthIdentity ));

            if ( pszUser != NULL )
            {
                AuthIdentity.User       = (unsigned char *) pszUser;
                AuthIdentity.UserLength = strlen( pszUser );
            }

            if ( pszPassword != NULL )
            {
                AuthIdentity.Password       = (unsigned char *) pszPassword;
                AuthIdentity.PasswordLength = strlen( pszPassword );
            }

            if ( pszDomain != NULL )
            {
                AuthIdentity.Domain       = (unsigned char *) pszDomain;
                AuthIdentity.DomainLength = strlen( pszDomain );
            }

            AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
        }
        else
        {
            pAuthIdentity = NULL;
        }

        ss = sfProcs.pAcquireCredentialsHandle( NULL,    // New principal
                                       pszPackage,       // Package name
                                       SECPKG_CRED_OUTBOUND,
                                       NULL,             // Logon ID
                                       pAuthIdentity,    // Auth Data
                                       NULL,             // Get key func
                                       NULL,             // Get key arg
                                       &pAS->_hcred,
                                       &Lifetime );

        //
        //  Need to determine the max token size for this package
        //

        if ( ss == STATUS_SUCCESS )
        {
            pAS->_fHaveCredHandle = TRUE;
            ss = sfProcs.pQuerySecurityPackageInfo(
                        (char *) pszPackage,
                        &pspkg );
        }

        if ( ss != STATUS_SUCCESS )
        {
            SetLastError( ss );
            return FALSE;
        }

        pAS->_cbMaxToken = pspkg->cbMaxToken;

        sfProcs.pFreeContextBuffer( pspkg );

    }

    //
    //  Prepare our output buffer.  We use a temporary buffer because
    //  the real output buffer will most likely need to be uuencoded
    //

    if ( !BufferResize( &buff, pAS->_cbMaxToken ))
    {
        fSt = FALSE;
        goto ex;
    }

    OutBuffDesc.ulVersion = 0;
    OutBuffDesc.cBuffers  = 1;
    OutBuffDesc.pBuffers  = &OutSecBuff;

    OutSecBuff.cbBuffer   = pAS->_cbMaxToken;
    OutSecBuff.BufferType = SECBUFFER_TOKEN;
    OutSecBuff.pvBuffer   = BufferQueryPtr( &buff );

    //
    //  Prepare our Input buffer - Note the server is expecting the client's
    //  negotiation packet on the first call
    //

    if ( pBuffIn )
    {
        InBuffDesc.ulVersion = 0;
        InBuffDesc.cBuffers  = 1;
        InBuffDesc.pBuffers  = &InSecBuff;

        InSecBuff.cbBuffer   = cbBuffIn;
        InSecBuff.BufferType = SECBUFFER_TOKEN;
        InSecBuff.pvBuffer   = pBuffIn;
    }

    {
        //
        //  will return success when its done but we still
        //  need to send the out buffer if there are bytes to send
        //

        ss = sfProcs.pInitializeSecurityContext(
                                        &pAS->_hcred,
                                        pAS->_fNewConversation ? NULL :
                                                &pAS->_hctxt,
                                        TOKEN_SOURCE_NAME,
                                        0,
                                        0,
                                        SECURITY_NATIVE_DREP,
                                        pAS->_fNewConversation ? NULL :
                                                &InBuffDesc,
                                        0,
                                        &pAS->_hctxt,
                                        &OutBuffDesc,
                                        &ContextAttributes,
                                        &Lifetime );
    }

    if ( !SEC_SUCCESS( ss ) )
    {
        if ( ss == SEC_E_LOGON_DENIED )
            ss = ERROR_LOGON_FAILURE;

        SetLastError( ss );
        fSt = FALSE;
        goto ex;
    }

    pAS->_fHaveCtxtHandle = TRUE;

    //
    //  Now we just need to complete the token (if requested) and prepare
    //  it for shipping to the other side if needed
    //

    fReply = !!OutSecBuff.cbBuffer;

    if ( (ss == SEC_I_COMPLETE_NEEDED) ||
         (ss == SEC_I_COMPLETE_AND_CONTINUE) )
    {
        if ( sfProcs.pCompleteAuthToken != NULL )
        {
            ss = sfProcs.pCompleteAuthToken( &pAS->_hctxt,
                                    &OutBuffDesc );

            if ( !SEC_SUCCESS( ss ))
            {
                fSt = FALSE;
                goto ex;
            }
        }
        else
        {
            // if not supported

            fSt = FALSE;
            goto ex;
        }
    }

    //
    //  Format or copy to the output buffer if we need to reply
    //

    if ( fReply )
    {
        if ( pAS->_fUUEncodeData )
        {
            if ( !uuencode( (BYTE *) OutSecBuff.pvBuffer,
                            OutSecBuff.cbBuffer,
                            pbuffOut ))
            {
                fSt = FALSE;
                goto ex;
            }

            *pcbBuffOut = strlen( (CHAR *) BufferQueryPtr(pbuffOut) );
        }
        else
        {
            if ( !BufferResize( pbuffOut, OutSecBuff.cbBuffer ))
            {
                fSt = FALSE;
                goto ex;
            }

            memcpy( BufferQueryPtr(pbuffOut),
                    OutSecBuff.pvBuffer,
                    OutSecBuff.cbBuffer );

            *pcbBuffOut = OutSecBuff.cbBuffer;
        }
    }

    if ( pAS->_fNewConversation )
        pAS->_fNewConversation = FALSE;

    *pfNeedMoreData = ((ss == SEC_I_CONTINUE_NEEDED) ||
                       (ss == SEC_I_COMPLETE_AND_CONTINUE));

    fSt = TRUE;

ex:
    BufferTerminate( &buffData );
    BufferTerminate( &buff );

    return fSt;
}

BOOL CrackUserAndDomain(
    CHAR *   pszDomainAndUser,
    CHAR * * ppszUser,
    CHAR * * ppszDomain
    )
/*++

Routine Description:

    Given a user name potentially in the form domain\user, zero terminates
    the domain name and returns pointers to the domain name and the user name

Arguments:

    pszDomainAndUser - Pointer to user name or domain and user name
    ppszUser - Receives pointer to user portion of name
    ppszDomain - Receives pointer to domain portion of name

Return Value:

    TRUE if successful, FALSE otherwise (call GetLastError)

--*/
{
    static CHAR szDefaultDomain[MAX_COMPUTERNAME_LENGTH+1];
    DWORD cbN;

    //
    //  Crack the name into domain/user components.
    //

    *ppszDomain = pszDomainAndUser;
    *ppszUser   = strpbrk( pszDomainAndUser, "/\\" );

    if( *ppszUser == NULL )
    {
        //
        //  No domain name specified, just the username so we assume the
        //  user is on the local machine
        //

        if ( !*szDefaultDomain )
        {
            cbN = sizeof(szDefaultDomain);
            GetComputerName( szDefaultDomain, &cbN );
        }

        *ppszDomain = szDefaultDomain;
        *ppszUser   = pszDomainAndUser;
    }
    else
    {
        //
        //  Both domain & user specified, skip delimiter.
        //

        **ppszUser = '\0';
        (*ppszUser)++;

        if( ( **ppszUser == '\0' ) ||
            ( **ppszUser == '\\' ) ||
            ( **ppszUser == '/' ) )
        {
            //
            //  Name is of one of the following (invalid) forms:
            //
            //      "domain\"
            //      "domain\\..."
            //      "domain/..."
            //

            SetLastError( ERROR_INVALID_PARAMETER );
            return FALSE;
        }
    }

    return TRUE;
}

/************************************************************
 *    uuencode/decode functions
 ************************************************************/

//
//  Taken from NCSA HTTP and wwwlib.
//
//  NOTE: These conform to RFC1113, which is slightly different then the Unix
//        uuencode and uudecode!
//

const int pr2six[256]={
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
    52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9,
    10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
    28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64
};

char six2pr[64] = {
    'A','B','C','D','E','F','G','H','I','J','K','L','M',
    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
    'a','b','c','d','e','f','g','h','i','j','k','l','m',
    'n','o','p','q','r','s','t','u','v','w','x','y','z',
    '0','1','2','3','4','5','6','7','8','9','+','/'
};

BOOL
uudecode(
    char   * bufcoded,
    BUFFER * pbuffdecoded,
    DWORD  * pcbDecoded )
/*++

 Routine Description:

    uudecode a string of data

 Arguments:

    bufcoded            pointer to uuencoded data
    pbuffdecoded        pointer to output BUFFER structure
    pcbDecoded          number of decode bytes

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
    int nbytesdecoded;
    char *bufin = bufcoded;
    unsigned char *bufout;
    int nprbytes;

    /* Strip leading whitespace. */

    while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;

    /* Figure out how many characters are in the input buffer.
     * If this would decode into more bytes than would fit into
     * the output buffer, adjust the number of input bytes downwards.
     */
    bufin = bufcoded;
    while(pr2six[*(bufin++)] <= 63);
    nprbytes = bufin - bufcoded - 1;
    nbytesdecoded = ((nprbytes+3)/4) * 3;

    if ( !BufferResize( pbuffdecoded, nbytesdecoded + 4 ))
        return FALSE;

    if ( pcbDecoded )
        *pcbDecoded = nbytesdecoded;

    bufout = (unsigned char *) BufferQueryPtr(pbuffdecoded);

    bufin = bufcoded;

    while (nprbytes > 0) {
        *(bufout++) =

(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
        *(bufout++) =
            (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
        *(bufout++) =
            (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
        bufin += 4;
        nprbytes -= 4;
    }

    if(nprbytes & 03) {
        if(pr2six[bufin[-2]] > 63)
            nbytesdecoded -= 2;
        else
            nbytesdecoded -= 1;
    }

    ((CHAR *)BufferQueryPtr(pbuffdecoded))[nbytesdecoded] = '\0';

    return TRUE;
}

BOOL
uuencode(
    BYTE *   bufin,
    DWORD    nbytes,
    BUFFER * pbuffEncoded )
/*++

 Routine Description:

    uuencode a string of data

 Arguments:

    bufin           pointer to data to encode
    nbytes          number of bytes to encode
    pbuffEncoded    pointer to output BUFFER structure

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
   unsigned char *outptr;
   unsigned int i;

   //
   //  Resize the buffer to 133% of the incoming data
   //

   if ( !BufferResize( pbuffEncoded, nbytes + ((nbytes + 3) / 3) + 4))
        return FALSE;

   outptr = (unsigned char *) BufferQueryPtr(pbuffEncoded);

   for (i=0; i<nbytes; i += 3) {
      *(outptr++) = six2pr[*bufin >> 2];            /* c1 */
      *(outptr++) = six2pr[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/
      *(outptr++) = six2pr[((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)];/*c3*/
      *(outptr++) = six2pr[bufin[2] & 077];         /* c4 */

      bufin += 3;
   }

   /* If nbytes was not a multiple of 3, then we have encoded too
    * many characters.  Adjust appropriately.
    */
   if(i == nbytes+1) {
      /* There were only 2 bytes in that last group */
      outptr[-1] = '=';
   } else if(i == nbytes+2) {
      /* There was only 1 byte in that last group */
      outptr[-1] = '=';
      outptr[-2] = '=';
   }

   *outptr = '\0';

   return TRUE;
}


HTTPAUTH.H

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    httpauth.h

Abstract:

    Functions for authentication sequence ( Basic & NTLM )

Revision History:

--*/

BOOL AddAuthorizationHeader(PSTR pch, PSTR pchSchemes, PSTR pchAuthData, PSTR pchUserName, PSTR pchPassword, BOOL *pfNeedMoreData );
BOOL InitAuthorizationHeader();
void TerminateAuthorizationHeader();
BOOL IsInAuthorizationSequence();
BOOL ValidateAuthenticationMethods( PSTR pszMet, PSTR pszPref );


HTTPGET.C

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    httpget.c

Abstract:

    This is a web command line application.  It will allow a user to get a
    html document from the command line.

Environment:

    console app

--*/

#include <windows.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <malloc.h>
#include "const.h"
#include "proto.h"

void
PrintUsage(void);

#define SOCKETS_METHOD 1

void
_CRTAPI1
main(
    int argc,
    char * argv[])
{
    char *        Server;
    char *        URL;
    char *        Verb = "GET";
    char *        Gateway = NULL;
    char *        AcceptTypes[2] = {"*/*", NULL};
    char          Headers[] =
                      "Accept: */*\r\n"
                      "User-Agent: Httpget\r\n"
                      "Referer: Httpget\r\n"
                      "\r\n";
    int           Method = SOCKETS_METHOD;
    BOOL          DisplayHeaders = FALSE;
    DWORD         ClientDataSize = 0;
    PSTR          pszUserName = "";
    PSTR          pszPassword = "";
    PSTR          pszStore = NULL;
    PSTR          pszPref = NULL;

    //
    // Parse the command line
    //

    if (argc < 3)
    {
        PrintUsage();
        return;
    }

    while (argc > 3)
    {
        //
        // parse options
        //

        if (argv[1][0] == '-')
        {
            switch (argv[1][1])
            {
            case 'V' :
            case 'v' :
                //
                // Input verb
                //

                Verb = &argv[1][3];

                break;

            case 'H' :
            case 'h' :
                //
                // Display headers
                //

                DisplayHeaders = TRUE;

                break;

            case 'D' :
            case 'd' :
                //
                // Amount of data to send
                //

                if (sscanf(&argv[1][3], "%u", &ClientDataSize) != 1)
                {
                    PrintUsage();
                    return;
                }
                break;

            case 'G' :
            case 'g' :
                //
                // Gateway
                //

                Gateway = &argv[1][3];

                break;

            case 'M':
            case 'm':
                // User name
                pszPref = &argv[1][3];
                break;

            case 'N':
            case 'n':
                // User name
                pszUserName = &argv[1][3];
                break;

            case 'P':
            case 'p':
                // Password
                pszPassword = &argv[1][3];
                break;

            case 'S':
            case 's':
                pszStore = &argv[1][3];
                break;

            default:
                PrintUsage();
                return;
                break;
            }
        }
        else
        {
            PrintUsage();
            return;
        }

        argc --;
        argv ++;
    }

    Server = argv[1];

    URL = argv[2];

    switch (Method)
    {
    case SOCKETS_METHOD:
        HttpGetSocket(
            Verb,
            Server,
            URL,
            DisplayHeaders,
            ClientDataSize,
            pszUserName,
            pszPassword,
            pszStore,
            pszPref );
        break;
    }

    return;
}

void
PrintUsage()
{
    fprintf(stderr,
        "httpauth  [-h] [-d:<size>] [-m:<methodlist>] [-v:<verb>] [-n:<username>]\n"
"\t  [-p:<password>] [-g:gateway] [-s:storefile] <server> <path>\n"
        "\t-h           - display result headers\n"
        "\t<size>       - amount of client data to send\n"
        "\t<verb>       - HTTP verb to use (default is GET)\n"
        "\t<username>   - user name for authentication\n"
        "\t<password>   - password for authentication\n"
        "\t<methodlist> - comma separated list of authentication methods in order\n"
        "\t               of preference (Default is to use first supported method\n"
        "\t               returned by the HTTP server (e.g., -m:NTLM,BASIC)\n"
        "\t<storefile>  - file where to store result message body\n"
        "\t<server>     - web server to connect to (without http:)\n"
        "\t<path>       - resource to get (e.g., /default.htm)\n"
    );
}


PROTO.H

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    proto.h

Abstract:

    prototypes

Environment:

    Internet

Revision History:

    dd-mmm-yyy <email>

--*/

BOOL
HttpGetSocket(
    char * Verb,
    char * Server,
    char * URL,
    BOOL   DisplayHeaders,
    DWORD  ClientDataSize,
    PSTR   pszUserName,
    PSTR   pszPassword,
    PSTR   pszStore,
    PSTR   pszPref );


CONST.H

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    const.h

Abstract:

    Constants

Environment:

    Internet

Revision History:

    dd-mmm-yyy <email>

--*/

#define VERBOSE 1


SSPI.H

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992-1997.
//
//  File:       sspi.h
//
//  Contents:   Security Support Provider Interface
//              Prototypes and structure definitions
//
//  Functions:  Security Support Provider API
//
//  History:    11-24-93   RichardW   Created
//
//----------------------------------------------------------------------------

#ifndef __SSPI_H__
#define __SSPI_H__

//
// Determine environment:
//

#ifdef SECURITY_WIN32
#define ISSP_LEVEL  32
#define ISSP_MODE   1
#endif // SECURITY_WIN32

#ifdef SECURITY_WIN16
#define ISSP_LEVEL  16
#define ISSP_MODE   1
#endif // SECURITY_WIN16

#ifdef SECURITY_KERNEL
#define ISSP_LEVEL  32

//
// SECURITY_KERNEL trumps SECURITY_WIN32.  Undefine ISSP_MODE so that
// we don't get redefine errors.
//
#ifdef ISSP_MODE
#undef ISSP_MODE
#endif
#define ISSP_MODE   0
#endif // SECURITY_KERNEL

#ifdef SECURITY_OS212
#define ISSP_LEVEL  16
#define ISSP_MODE   1
#endif // SECURITY_OS212

#ifdef SECURITY_DOS
#define ISSP_LEVEL  16
#define ISSP_MODE   1
#endif // SECURITY_DOS

#ifdef SECURITY_MAC
#define ISSP_LEVEL  32
#define ISSP_MODE   1
#endif // SECURITY_MAC

#ifndef ISSP_LEVEL
#error  You must define one of SECURITY_WIN32, SECURITY_WIN16, SECURITY_KERNEL
#error  SECURITY_DOS, SECURITY_MAC or SECURITY_OS212
#endif // !ISSP_LEVEL

//
// Now, define platform specific mappings:
//

#if ISSP_LEVEL == 16

typedef short SECURITY_STATUS;
typedef unsigned short SEC_WCHAR;
typedef char SEC_CHAR;
#define SEC_TEXT(_x_) _x_

#ifdef SECURITY_WIN16

#define SEC_FAR __far
#define SEC_ENTRY __pascal __far __export

#else // SECURITY_WIN16

#define SEC_FAR __far
#define SEC_ENTRY __pascal __far __loadds
#pragma warning(disable:4147)

#endif // SECURITY_WIN16

#elif defined(SECURITY_MAC)  // ISSP_LEVEL == 16

#define SEC_ENTRY
#define SEC_TEXT(_X_) _X_
#define SEC_FAR

typedef unsigned short SEC_WCHAR;
typedef char SEC_CHAR;
typedef long SECURITY_STATUS;

// No Unicode on the Mac

typedef SEC_CHAR SEC_FAR * SECURITY_PSTR;
typedef SEC_CHAR SEC_FAR * SECURITY_PCSTR;

#else // ISSP_LEVEL == 16

//
// For NT-2 and up, wtypes will define HRESULT to be long.
//

// begin_ntifs

typedef WCHAR SEC_WCHAR;
typedef CHAR SEC_CHAR;

typedef LONG SECURITY_STATUS;

#define SEC_TEXT TEXT
#define SEC_FAR
#define SEC_ENTRY __stdcall

// end_ntifs

//
// Decide what a string - 32 bits only since for 16 bits it is clear.
//

#ifdef UNICODE
typedef SEC_WCHAR SEC_FAR * SECURITY_PSTR;
typedef CONST SEC_WCHAR SEC_FAR * SECURITY_PCSTR;
#else // UNICODE
typedef SEC_CHAR SEC_FAR * SECURITY_PSTR;
typedef CONST SEC_CHAR SEC_FAR * SECURITY_PCSTR;
#endif // UNICODE

#endif // ISSP_LEVEL == 16

//
// Equivalent string for rpcrt:
//

#define __SEC_FAR SEC_FAR

//
// Okay, security specific types:
//

// begin_ntifs

typedef struct _SecHandle
{
    unsigned long dwLower;
    unsigned long dwUpper;
} SecHandle, SEC_FAR * PSecHandle;

typedef SecHandle CredHandle;
typedef PSecHandle PCredHandle;

typedef SecHandle CtxtHandle;
typedef PSecHandle PCtxtHandle;

// end_ntifs

#if ISSP_LEVEL == 32

#  ifdef WIN32_CHICAGO

typedef unsigned __int64 QWORD;
typedef QWORD SECURITY_INTEGER, *PSECURITY_INTEGER;
#define SEC_SUCCESS(Status) ((Status) >= 0)

#  elif defined(_NTDEF_) || defined(_WINNT_)

typedef LARGE_INTEGER _SECURITY_INTEGER, SECURITY_INTEGER, *PSECURITY_INTEGER; // ntifs

#  else // _NTDEF_ || _WINNT_

// BUGBUG:  Alignment for axp

typedef struct _SECURITY_INTEGER
{
    unsigned long LowPart;
    long HighPart;
} SECURITY_INTEGER, *PSECURITY_INTEGER;

#  endif // _NTDEF_ || _WINNT_

#  ifndef SECURITY_MAC

typedef SECURITY_INTEGER TimeStamp;                 // ntifs
typedef SECURITY_INTEGER SEC_FAR * PTimeStamp;      // ntifs

#  else // SECURITY_MAC
typedef unsigned long TimeStamp;
typedef unsigned long * PTimeStamp;
#  endif // SECUIRT_MAC

#else // ISSP_LEVEL == 32

typedef unsigned long TimeStamp;
typedef unsigned long SEC_FAR * PTimeStamp;
#  ifdef WIN32_CHICAGO
typedef TimeStamp LARGE_INTEGER;
#endif // WIN32_CHICAGO

#endif // ISSP_LEVEL == 32

//
// If we are in 32 bit mode, define the SECURITY_STRING structure,
// as a clone of the base UNICODE_STRING structure.  This is used
// internally in security components, an as the string interface
// for kernel components (e.g. FSPs)
//

#if ISSP_LEVEL == 32
#  ifndef _NTDEF_
typedef struct _SECURITY_STRING {
    unsigned short      Length;
    unsigned short      MaximumLength;
#    ifdef MIDL_PASS
    [size_is(MaximumLength / 2), length_is(Length / 2)]
#    endif // MIDL_PASS
    unsigned short *    Buffer;
} SECURITY_STRING, * PSECURITY_STRING;
#  else // _NTDEF_
typedef UNICODE_STRING SECURITY_STRING, *PSECURITY_STRING;  // ntifs
#  endif // _NTDEF_
#endif // ISSP_LEVEL == 32

// begin_ntifs

//
// SecPkgInfo structure
//
//  Provides general information about a security provider
//

typedef struct _SecPkgInfoW
{
    unsigned long fCapabilities;        // Capability bitmask
    unsigned short wVersion;            // Version of driver
    unsigned short wRPCID;              // ID for RPC Runtime
    unsigned long cbMaxToken;           // Size of authentication token (max)
#ifdef MIDL_PASS
    [string]
#endif
    SEC_WCHAR SEC_FAR * Name;           // Text name

#ifdef MIDL_PASS
    [string]
#endif
    SEC_WCHAR SEC_FAR * Comment;        // Comment
} SecPkgInfoW, SEC_FAR * PSecPkgInfoW;

// end_ntifs

typedef struct _SecPkgInfoA
{
    unsigned long fCapabilities;        // Capability bitmask
    unsigned short wVersion;            // Version of driver
    unsigned short wRPCID;              // ID for RPC Runtime
    unsigned long cbMaxToken;           // Size of authentication token (max)
#ifdef MIDL_PASS
    [string]
#endif
    SEC_CHAR SEC_FAR * Name;            // Text name

#ifdef MIDL_PASS
    [string]
#endif
    SEC_CHAR SEC_FAR * Comment;         // Comment
} SecPkgInfoA, SEC_FAR * PSecPkgInfoA;

#ifdef UNICODE
#  define SecPkgInfo SecPkgInfoW        // ntifs
#  define PSecPkgInfo PSecPkgInfoW      // ntifs
#else
#  define SecPkgInfo SecPkgInfoA
#  define PSecPkgInfo PSecPkgInfoA
#endif // !UNICODE

// begin_ntifs

//
//  Security Package Capabilities
//
#define SECPKG_FLAG_INTEGRITY       0x00000001  // Supports integrity on messages
#define SECPKG_FLAG_PRIVACY         0x00000002  // Supports privacy (confidentiality)
#define SECPKG_FLAG_TOKEN_ONLY      0x00000004  // Only security token needed
#define SECPKG_FLAG_DATAGRAM        0x00000008  // Datagram RPC support
#define SECPKG_FLAG_CONNECTION      0x00000010  // Connection oriented RPC support
#define SECPKG_FLAG_MULTI_REQUIRED  0x00000020  // Full 3-leg required for re-auth.
#define SECPKG_FLAG_CLIENT_ONLY     0x00000040  // Server side functionality not available
#define SECPKG_FLAG_EXTENDED_ERROR  0x00000080  // Supports extended error msgs
#define SECPKG_FLAG_IMPERSONATION   0x00000100  // Supports impersonation
#define SECPKG_FLAG_ACCEPT_WIN32_NAME   0x00000200  // Accepts Win32 names
#define SECPKG_FLAG_STREAM          0x00000400  // Supports stream semantics
#define SECPKG_FLAG_NEGOTIABLE      0x00000800  // Can be used by the negotiate package
#define SECPKG_FLAG_GSS_COMPATIBLE  0x00001000  // GSS Compatibility Available
#define SECPKG_FLAG_LOGON           0x00002000  // Supports common LsaLogonUser

#define SECPKG_ID_NONE      0xFFFF

//
// SecBuffer
//
//  Generic memory descriptors for buffers passed in to the security
//  API
//

typedef struct _SecBuffer {
    unsigned long cbBuffer;             // Size of the buffer, in bytes
    unsigned long BufferType;           // Type of the buffer (below)
    void SEC_FAR * pvBuffer;            // Pointer to the buffer
} SecBuffer, SEC_FAR * PSecBuffer;

typedef struct _SecBufferDesc {
    unsigned long ulVersion;            // Version number
    unsigned long cBuffers;             // Number of buffers
#ifdef MIDL_PASS
    [size_is(cBuffers)]
#endif
    PSecBuffer pBuffers;                // Pointer to array of buffers
} SecBufferDesc, SEC_FAR * PSecBufferDesc;

#define SECBUFFER_VERSION           0

#define SECBUFFER_EMPTY             0   // Undefined, replaced by provider
#define SECBUFFER_DATA              1   // Packet data
#define SECBUFFER_TOKEN             2   // Security token
#define SECBUFFER_PKG_PARAMS        3   // Package specific parameters
#define SECBUFFER_MISSING           4   // Missing Data indicator
#define SECBUFFER_EXTRA             5   // Extra data
#define SECBUFFER_STREAM_TRAILER    6   // Security Trailer
#define SECBUFFER_STREAM_HEADER     7   // Security Header
#define SECBUFFER_NEGOTIATION_INFO  8   // Hints from the negotiation pkg

#define SECBUFFER_ATTRMASK          0xF0000000
#define SECBUFFER_READONLY          0x80000000  // Buffer is read-only
#define SECBUFFER_RESERVED          0x40000000

typedef struct _SEC_NEGOTIATION_INFO {
    unsigned long       Size;           // Size of this structure
    unsigned long       NameLength;     // Length of name hint
    SEC_WCHAR SEC_FAR * Name;           // Name hint
    void SEC_FAR *      Reserved;       // Reserved
} SEC_NEGOTIATION_INFO, SEC_FAR * PSEC_NEGOTIATION_INFO ;

//
//  Data Representation Constant:
//
#define SECURITY_NATIVE_DREP        0x00000010
#define SECURITY_NETWORK_DREP       0x00000000

//
//  Credential Use Flags
//
#define SECPKG_CRED_INBOUND         0x00000001
#define SECPKG_CRED_OUTBOUND        0x00000002
#define SECPKG_CRED_BOTH            0x00000003

//
//  InitializeSecurityContext Requirement and return flags:
//

#define ISC_REQ_DELEGATE                0x00000001
#define ISC_REQ_MUTUAL_AUTH             0x00000002
#define ISC_REQ_REPLAY_DETECT           0x00000004
#define ISC_REQ_SEQUENCE_DETECT         0x00000008
#define ISC_REQ_CONFIDENTIALITY         0x00000010
#define ISC_REQ_USE_SESSION_KEY         0x00000020
#define ISC_REQ_PROMPT_FOR_CREDS        0x00000040
#define ISC_REQ_USE_SUPPLIED_CREDS      0x00000080
#define ISC_REQ_ALLOCATE_MEMORY         0x00000100
#define ISC_REQ_USE_DCE_STYLE           0x00000200
#define ISC_REQ_DATAGRAM                0x00000400
#define ISC_REQ_CONNECTION              0x00000800
#define ISC_REQ_CALL_LEVEL              0x00001000
#define ISC_REQ_EXTENDED_ERROR          0x00004000
#define ISC_REQ_STREAM                  0x00008000
#define ISC_REQ_INTEGRITY               0x00010000
#define ISC_REQ_IDENTIFY                0x00020000
#define ISC_REQ_NULL_SESSION            0x00040000

#define ISC_RET_DELEGATE                0x00000001
#define ISC_RET_MUTUAL_AUTH             0x00000002
#define ISC_RET_REPLAY_DETECT           0x00000004
#define ISC_RET_SEQUENCE_DETECT         0x00000008
#define ISC_RET_CONFIDENTIALITY         0x00000010
#define ISC_RET_USE_SESSION_KEY         0x00000020
#define ISC_RET_USED_COLLECTED_CREDS    0x00000040
#define ISC_RET_USED_SUPPLIED_CREDS     0x00000080
#define ISC_RET_ALLOCATED_MEMORY        0x00000100
#define ISC_RET_USED_DCE_STYLE          0x00000200
#define ISC_RET_DATAGRAM                0x00000400
#define ISC_RET_CONNECTION              0x00000800
#define ISC_RET_INTERMEDIATE_RETURN     0x00001000
#define ISC_RET_CALL_LEVEL              0x00002000
#define ISC_RET_EXTENDED_ERROR          0x00004000
#define ISC_RET_STREAM                  0x00008000
#define ISC_RET_INTEGRITY               0x00010000
#define ISC_RET_IDENTIFY                0x00020000
#define ISC_RET_NULL_SESSION            0x00040000

#define ASC_REQ_DELEGATE                0x00000001
#define ASC_REQ_MUTUAL_AUTH             0x00000002
#define ASC_REQ_REPLAY_DETECT           0x00000004
#define ASC_REQ_SEQUENCE_DETECT         0x00000008
#define ASC_REQ_CONFIDENTIALITY         0x00000010
#define ASC_REQ_USE_SESSION_KEY         0x00000020
#define ASC_REQ_ALLOCATE_MEMORY         0x00000100
#define ASC_REQ_USE_DCE_STYLE           0x00000200
#define ASC_REQ_DATAGRAM                0x00000400
#define ASC_REQ_CONNECTION              0x00000800
#define ASC_REQ_CALL_LEVEL              0x00001000
#define ASC_REQ_EXTENDED_ERROR          0x00008000
#define ASC_REQ_STREAM                  0x00010000
#define ASC_REQ_INTEGRITY               0x00020000
#define ASC_REQ_LICENSING               0x00040000
#define ASC_REQ_IDENTIFY                0x00080000
#define ASC_REQ_ALLOW_NULL_SESSION      0x00100000

#define ASC_RET_DELEGATE                0x00000001
#define ASC_RET_MUTUAL_AUTH             0x00000002
#define ASC_RET_REPLAY_DETECT           0x00000004
#define ASC_RET_SEQUENCE_DETECT         0x00000008
#define ASC_RET_CONFIDENTIALITY         0x00000010
#define ASC_RET_USE_SESSION_KEY         0x00000020
#define ASC_RET_ALLOCATED_MEMORY        0x00000100
#define ASC_RET_USED_DCE_STYLE          0x00000200
#define ASC_RET_DATAGRAM                0x00000400
#define ASC_RET_CONNECTION              0x00000800
#define ASC_RET_CALL_LEVEL              0x00002000 // skipped 1000 to be like ISC_
#define ASC_RET_THIRD_LEG_FAILED        0x00004000
#define ASC_RET_EXTENDED_ERROR          0x00008000
#define ASC_RET_STREAM                  0x00010000
#define ASC_RET_INTEGRITY               0x00020000
#define ASC_RET_LICENSING               0x00040000
#define ASC_RET_IDENTIFY                0x00080000
#define ASC_RET_NULL_SESSION            0x00100000

//
//  Security Credentials Attributes:
//

#define SECPKG_CRED_ATTR_NAMES 1

typedef struct _SecPkgCredentials_NamesW
{
    SEC_WCHAR SEC_FAR * sUserName;
} SecPkgCredentials_NamesW, SEC_FAR * PSecPkgCredentials_NamesW;

// end_ntifs

typedef struct _SecPkgCredentials_NamesA
{
    SEC_CHAR SEC_FAR * sUserName;
} SecPkgCredentials_NamesA, SEC_FAR * PSecPkgCredentials_NamesA;

#ifdef UNICODE
#  define SecPkgCredentials_Names SecPkgCredentials_NamesW      // ntifs
#  define PSecPkgCredentials_Names PSecPkgCredentials_NamesW    // ntifs
#else
#  define SecPkgCredentials_Names SecPkgCredentials_NamesA
#  define PSecPkgCredentials_Names PSecPkgCredentials_NamesA
#endif // !UNICODE

// begin_ntifs

//
//  Security Context Attributes:
//

#define SECPKG_ATTR_SIZES           0
#define SECPKG_ATTR_NAMES           1
#define SECPKG_ATTR_LIFESPAN        2
#define SECPKG_ATTR_DCE_INFO        3
#define SECPKG_ATTR_STREAM_SIZES    4
#define SECPKG_ATTR_KEY_INFO        5
#define SECPKG_ATTR_AUTHORITY       6
#define SECPKG_ATTR_PROTO_INFO      7
#define SECPKG_ATTR_PASSWORD_EXPIRY 8
#define SECPKG_ATTR_SESSION_KEY     9
#define SECPKG_ATTR_PACKAGE_INFO    10

typedef struct _SecPkgContext_Sizes
{
    unsigned long cbMaxToken;
    unsigned long cbMaxSignature;
    unsigned long cbBlockSize;
    unsigned long cbSecurityTrailer;
} SecPkgContext_Sizes, SEC_FAR * PSecPkgContext_Sizes;

typedef struct _SecPkgContext_StreamSizes
{
    unsigned long   cbHeader;
    unsigned long   cbTrailer;
    unsigned long   cbMaximumMessage;
    unsigned long   cBuffers;
    unsigned long   cbBlockSize;
} SecPkgContext_StreamSizes, * PSecPkgContext_StreamSizes;

typedef struct _SecPkgContext_NamesW
{
    SEC_WCHAR SEC_FAR * sUserName;
} SecPkgContext_NamesW, SEC_FAR * PSecPkgContext_NamesW;

// end_ntifs

typedef struct _SecPkgContext_NamesA
{
    SEC_CHAR SEC_FAR * sUserName;
} SecPkgContext_NamesA, SEC_FAR * PSecPkgContext_NamesA;

#ifdef UNICODE
#  define SecPkgContext_Names SecPkgContext_NamesW          // ntifs
#  define PSecPkgContext_Names PSecPkgContext_NamesW        // ntifs
#else
#  define SecPkgContext_Names SecPkgContext_NamesA
#  define PSecPkgContext_Names PSecPkgContext_NamesA
#endif // !UNICODE

// begin_ntifs

typedef struct _SecPkgContext_Lifespan
{
    TimeStamp tsStart;
    TimeStamp tsExpiry;
} SecPkgContext_Lifespan, SEC_FAR * PSecPkgContext_Lifespan;

typedef struct _SecPkgContext_DceInfo
{
    unsigned long AuthzSvc;
    void SEC_FAR * pPac;
} SecPkgContext_DceInfo, SEC_FAR * PSecPkgContext_DceInfo;

// end_ntifs

typedef struct _SecPkgContext_KeyInfoA
{
    SEC_CHAR SEC_FAR *  sSignatureAlgorithmName;
    SEC_CHAR SEC_FAR *  sEncryptAlgorithmName;
    unsigned long       KeySize;
    unsigned long       SignatureAlgorithm;
    unsigned long       EncryptAlgorithm;
} SecPkgContext_KeyInfoA, SEC_FAR * PSecPkgContext_KeyInfoA;

// begin_ntifs

typedef struct _SecPkgContext_KeyInfoW
{
    SEC_WCHAR SEC_FAR * sSignatureAlgorithmName;
    SEC_WCHAR SEC_FAR * sEncryptAlgorithmName;
    unsigned long       KeySize;
    unsigned long       SignatureAlgorithm;
    unsigned long       EncryptAlgorithm;
} SecPkgContext_KeyInfoW, SEC_FAR * PSecPkgContext_KeyInfoW;

// end_ntifs

#ifdef UNICODE
#define SecPkgContext_KeyInfo   SecPkgContext_KeyInfoW      // ntifs
#define PSecPkgContext_KeyInfo  PSecPkgContext_KeyInfoW     // ntifs
#else
#define SecPkgContext_KeyInfo   SecPkgContext_KeyInfoA
#define PSecPkgContext_KeyInfo  PSecPkgContext_KeyInfoA
#endif

typedef struct _SecPkgContext_AuthorityA
{
    SEC_CHAR SEC_FAR *  sAuthorityName;
} SecPkgContext_AuthorityA, * PSecPkgContext_AuthorityA;

// begin_ntifs

typedef struct _SecPkgContext_AuthorityW
{
    SEC_WCHAR SEC_FAR * sAuthorityName;
} SecPkgContext_AuthorityW, * PSecPkgContext_AuthorityW;

// end_ntifs

#ifdef UNICODE
#define SecPkgContext_Authority SecPkgContext_AuthorityW        // ntifs
#define PSecPkgContext_Authority    PSecPkgContext_AuthorityW   // ntifs
#else
#define SecPkgContext_Authority SecPkgContext_AuthorityA
#define PSecPkgContext_Authority    PSecPkgContext_AuthorityA
#endif

typedef struct _SecPkgContext_ProtoInfoA
{
    SEC_CHAR SEC_FAR *  sProtocolName;
    unsigned long       majorVersion;
    unsigned long       minorVersion;
} SecPkgContext_ProtoInfoA, SEC_FAR * PSecPkgContext_ProtoInfoA;

// begin_ntifs

typedef struct _SecPkgContext_ProtoInfoW
{
    SEC_WCHAR SEC_FAR * sProtocolName;
    unsigned long       majorVersion;
    unsigned long       minorVersion;
} SecPkgContext_ProtoInfoW, SEC_FAR * PSecPkgContext_ProtoInfoW;

// end_ntifs

#ifdef UNICODE
#define SecPkgContext_ProtoInfo   SecPkgContext_ProtoInfoW      // ntifs
#define PSecPkgContext_ProtoInfo  PSecPkgContext_ProtoInfoW     // ntifs
#else
#define SecPkgContext_ProtoInfo   SecPkgContext_ProtoInfoA
#define PSecPkgContext_ProtoInfo  PSecPkgContext_ProtoInfoA
#endif

// begin_ntifs

typedef struct _SecPkgContext_PasswordExpiry
{
    TimeStamp tsPasswordExpires;
} SecPkgContext_PasswordExpiry, SEC_FAR * PSecPkgContext_PasswordExpiry;

typedef struct _SecPkgContext_SessionKey
{
    unsigned long SessionKeyLength;
    unsigned char SEC_FAR * SessionKey;
} SecPkgContext_SessionKey, *PSecPkgContext_SessionKey;

// end_ntifs
// begin_ntifs

typedef struct _SecPkgContext_PackageInfoW
{
    PSecPkgInfoW PackageInfo;
} SecPkgContext_PackageInfoW, SEC_FAR * PSecPkgContext_PackageInfoW;

// end_ntifs

typedef struct _SecPkgContext_PackageInfoA
{
    PSecPkgInfoA PackageInfo;
} SecPkgContext_PackageInfoA, SEC_FAR * PSecPkgContext_PackageInfoA;

#ifdef UNICODE
#define SecPkgContext_PackageInfo   SecPkgContext_PackageInfoW      // ntifs
#define PSecPkgContext_PackageInfo  PSecPkgContext_PackageInfoW     // ntifs
#else
#define SecPkgContext_PackageInfo   SecPkgContext_PackageInfoA
#define PSecPkgContext_PackageInfo  PSecPkgContext_PackageInfoA
#endif

// begin_ntifs

typedef void
(SEC_ENTRY SEC_FAR * SEC_GET_KEY_FN) (
    void SEC_FAR * Arg,                 // Argument passed in
    void SEC_FAR * Principal,           // Principal ID
    unsigned long KeyVer,               // Key Version
    void SEC_FAR * SEC_FAR * Key,       // Returned ptr to key
    SECURITY_STATUS SEC_FAR * Status    // returned status
    );

//
// Flags for ExportSecurityContext
//

#define SECPKG_CONTEXT_EXPORT_RESET_NEW         0x00000001      // New context is reset to initial state
#define SECPKG_CONTEXT_EXPORT_DELETE_OLD        0x00000002      // Old context is deleted during export

SECURITY_STATUS SEC_ENTRY
AcquireCredentialsHandleW(
#if ISSP_MODE == 0                      // For Kernel mode
    PSECURITY_STRING pPrincipal,
    PSECURITY_STRING pPackage,
#else
    SEC_WCHAR SEC_FAR * pszPrincipal,   // Name of principal
    SEC_WCHAR SEC_FAR * pszPackage,     // Name of package
#endif
    unsigned long fCredentialUse,       // Flags indicating use
    void SEC_FAR * pvLogonId,           // Pointer to logon ID
    void SEC_FAR * pAuthData,           // Package specific data
    SEC_GET_KEY_FN pGetKeyFn,           // Pointer to GetKey() func
    void SEC_FAR * pvGetKeyArgument,    // Value to pass to GetKey()
    PCredHandle phCredential,           // (out) Cred Handle
    PTimeStamp ptsExpiry                // (out) Lifetime (optional)
    );

typedef SECURITY_STATUS
(SEC_ENTRY * ACQUIRE_CREDENTIALS_HANDLE_FN_W)(
#if ISSP_MODE == 0
    PSECURITY_STRING,
    PSECURITY_STRING,
#else
    SEC_WCHAR SEC_FAR *,
    SEC_WCHAR SEC_FAR *,
#endif
    unsigned long,
    void SEC_FAR *,
    void SEC_FAR *,
    SEC_GET_KEY_FN,
    void SEC_FAR *,
    PCredHandle,
    PTimeStamp);

// end_ntifs

SECURITY_STATUS SEC_ENTRY
AcquireCredentialsHandleA(
    SEC_CHAR SEC_FAR * pszPrincipal,    // Name of principal
    SEC_CHAR SEC_FAR * pszPackage,      // Name of package
    unsigned long fCredentialUse,       // Flags indicating use
    void SEC_FAR * pvLogonId,           // Pointer to logon ID
    void SEC_FAR * pAuthData,           // Package specific data
    SEC_GET_KEY_FN pGetKeyFn,           // Pointer to GetKey() func
    void SEC_FAR * pvGetKeyArgument,    // Value to pass to GetKey()
    PCredHandle phCredential,           // (out) Cred Handle
    PTimeStamp ptsExpiry                // (out) Lifetime (optional)
    );

typedef SECURITY_STATUS
(SEC_ENTRY * ACQUIRE_CREDENTIALS_HANDLE_FN_A)(
    SEC_CHAR SEC_FAR *,
    SEC_CHAR SEC_FAR *,
    unsigned long,
    void SEC_FAR *,
    void SEC_FAR *,
    SEC_GET_KEY_FN,
    void SEC_FAR *,
    PCredHandle,
    PTimeStamp);

#ifdef UNICODE
#  define AcquireCredentialsHandle AcquireCredentialsHandleW            // ntifs
#  define ACQUIRE_CREDENTIALS_HANDLE_FN ACQUIRE_CREDENTIALS_HANDLE_FN_W // ntifs
#else
#  define AcquireCredentialsHandle AcquireCredentialsHandleA
#  define ACQUIRE_CREDENTIALS_HANDLE_FN ACQUIRE_CREDENTIALS_HANDLE_FN_A
#endif // !UNICODE

// begin_ntifs

SECURITY_STATUS SEC_ENTRY
FreeCredentialsHandle(
    PCredHandle phCredential            // Handle to free
    );

typedef SECURITY_STATUS
(SEC_ENTRY * FREE_CREDENTIALS_HANDLE_FN)(
    PCredHandle );

#ifdef WIN32_CHICAGO
SECURITY_STATUS SEC_ENTRY
SspiLogonUserW(
    SEC_WCHAR SEC_FAR * pszPackage,     // Name of package
    SEC_WCHAR SEC_FAR * pszUserName,     // Name of package
    SEC_WCHAR SEC_FAR * pszDomainName,     // Name of package
    SEC_WCHAR SEC_FAR * pszPassword      // Name of package
    );

typedef SECURITY_STATUS
(SEC_ENTRY * SSPI_LOGON_USER_FN_W)(
    SEC_CHAR SEC_FAR *,
    SEC_CHAR SEC_FAR *,
    SEC_CHAR SEC_FAR *,
    SEC_CHAR SEC_FAR *);

SECURITY_STATUS SEC_ENTRY
SspiLogonUserA(
    SEC_CHAR SEC_FAR * pszPackage,     // Name of package
    SEC_CHAR SEC_FAR * pszUserName,     // Name of package
    SEC_CHAR SEC_FAR * pszDomainName,     // Name of package
    SEC_CHAR SEC_FAR * pszPassword      // Name of package
    );

typedef SECURITY_STATUS
(SEC_ENTRY * SSPI_LOGON_USER_FN_A)(
    SEC_CHAR SEC_FAR *,
    SEC_CHAR SEC_FAR *,
    SEC_CHAR SEC_FAR *,
    SEC_CHAR SEC_FAR *);

#ifdef UNICODE
#define SspiLogonUser SspiLogonUserW            // ntifs
#define SSPI_LOGON_USER_FN SSPI_LOGON_USER_FN_W
#else
#define SspiLogonUser SspiLogonUserA
#define SSPI_LOGON_USER_FN SSPI_LOGON_USER_FN_A
#endif // !UNICODE
#endif // WIN32_CHICAGO

// end_ntifs

// begin_ntifs

////////////////////////////////////////////////////////////////////////
///
/// Context Management Functions
///
////////////////////////////////////////////////////////////////////////

SECURITY_STATUS SEC_ENTRY
InitializeSecurityContextW(
    PCredHandle phCredential,               // Cred to base context
    PCtxtHandle phContext,                  // Existing context (OPT)
#if ISSP_MODE == 0
    PSECURITY_STRING pTargetName,
#else
    SEC_WCHAR SEC_FAR * pszTargetName,      // Name of target
#endif
    unsigned long fContextReq,              // Context Requirements
    unsigned long Reserved1,                // Reserved, MBZ
    unsigned long TargetDataRep,            // Data rep of target
    PSecBufferDesc pInput,                  // Input Buffers
    unsigned long Reserved2,                // Reserved, MBZ
    PCtxtHandle phNewContext,               // (out) New Context handle
    PSecBufferDesc pOutput,                 // (inout) Output Buffers
    unsigned long SEC_FAR * pfContextAttr,  // (out) Context attrs
    PTimeStamp ptsExpiry                    // (out) Life span (OPT)
    );

typedef SECURITY_STATUS
(SEC_ENTRY * INITIALIZE_SECURITY_CONTEXT_FN_W)(
    PCredHandle,
    PCtxtHandle,
#if ISSP_MODE == 0
    PSECURITY_STRING,
#else
    SEC_WCHAR SEC_FAR *,
#endif
    unsigned long,
    unsigned long,
    unsigned long,
    PSecBufferDesc,
    unsigned long,
    PCtxtHandle,
    PSecBufferDesc,
    unsigned long SEC_FAR *,
    PTimeStamp);

// end_ntifs

SECURITY_STATUS SEC_ENTRY
InitializeSecurityContextA(
    PCredHandle phCredential,               // Cred to base context
    PCtxtHandle phContext,                  // Existing context (OPT)
    SEC_CHAR SEC_FAR * pszTargetName,       // Name of target
    unsigned long fContextReq,              // Context Requirements
    unsigned long Reserved1,                // Reserved, MBZ
    unsigned long TargetDataRep,            // Data rep of target
    PSecBufferDesc pInput,                  // Input Buffers
    unsigned long Reserved2,                // Reserved, MBZ
    PCtxtHandle phNewContext,               // (out) New Context handle
    PSecBufferDesc pOutput,                 // (inout) Output Buffers
    unsigned long SEC_FAR * pfContextAttr,  // (out) Context attrs
    PTimeStamp ptsExpiry                    // (out) Life span (OPT)
    );

typedef SECURITY_STATUS
(SEC_ENTRY * INITIALIZE_SECURITY_CONTEXT_FN_A)(
    PCredHandle,
    PCtxtHandle,
    SEC_CHAR SEC_FAR *,
    unsigned long,
    unsigned long,
    unsigned long,
    PSecBufferDesc,
    unsigned long,
    PCtxtHandle,
    PSecBufferDesc,
    unsigned long SEC_FAR *,
    PTimeStamp);

#ifdef UNICODE
#  define InitializeSecurityContext InitializeSecurityContextW              // ntifs
#  define INITIALIZE_SECURITY_CONTEXT_FN INITIALIZE_SECURITY_CONTEXT_FN_W   // ntifs
#else
#  define InitializeSecurityContext InitializeSecurityContextA
#  define INITIALIZE_SECURITY_CONTEXT_FN INITIALIZE_SECURITY_CONTEXT_FN_A
#endif // !UNICODE

// begin_ntifs

SECURITY_STATUS SEC_ENTRY
AcceptSecurityContext(
    PCredHandle phCredential,               // Cred to base context
    PCtxtHandle phContext,                  // Existing context (OPT)
    PSecBufferDesc pInput,                  // Input buffer
    unsigned long fContextReq,              // Context Requirements
    unsigned long TargetDataRep,            // Target Data Rep
    PCtxtHandle phNewContext,               // (out) New context handle
    PSecBufferDesc pOutput,                 // (inout) Output buffers
    unsigned long SEC_FAR * pfContextAttr,  // (out) Context attributes
    PTimeStamp ptsExpiry                    // (out) Life span (OPT)
    );

typedef SECURITY_STATUS
(SEC_ENTRY * ACCEPT_SECURITY_CONTEXT_FN)(
    PCredHandle,
    PCtxtHandle,
    PSecBufferDesc,
    unsigned long,
    unsigned long,
    PCtxtHandle,
    PSecBufferDesc,
    unsigned long SEC_FAR *,
    PTimeStamp);

SECURITY_STATUS SEC_ENTRY
CompleteAuthToken(
    PCtxtHandle phContext,              // Context to complete
    PSecBufferDesc pToken               // Token to complete
    );

typedef SECURITY_STATUS
(SEC_ENTRY * COMPLETE_AUTH_TOKEN_FN)(
    PCtxtHandle,
    PSecBufferDesc);

SECURITY_STATUS SEC_ENTRY
ImpersonateSecurityContext(
    PCtxtHandle phContext               // Context to impersonate
    );

typedef SECURITY_STATUS
(SEC_ENTRY * IMPERSONATE_SECURITY_CONTEXT_FN)(
    PCtxtHandle);

SECURITY_STATUS SEC_ENTRY
RevertSecurityContext(
    PCtxtHandle phContext               // Context from which to re
    );

typedef SECURITY_STATUS

(SEC_ENTRY * REVERT_SECURITY_CONTEXT_FN)(
    PCtxtHandle);

SECURITY_STATUS SEC_ENTRY
QuerySecurityContextToken(
    PCtxtHandle phContext,
    void SEC_FAR * SEC_FAR * Token
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_SECURITY_CONTEXT_TOKEN_FN)(
    PCtxtHandle, void SEC_FAR * SEC_FAR *);

SECURITY_STATUS SEC_ENTRY
DeleteSecurityContext(
    PCtxtHandle phContext               // Context to delete
    );

typedef SECURITY_STATUS
(SEC_ENTRY * DELETE_SECURITY_CONTEXT_FN)(
    PCtxtHandle);

SECURITY_STATUS SEC_ENTRY
ApplyControlToken(
    PCtxtHandle phContext,              // Context to modify
    PSecBufferDesc pInput               // Input token to apply
    );

typedef SECURITY_STATUS
(SEC_ENTRY * APPLY_CONTROL_TOKEN_FN)(
    PCtxtHandle, PSecBufferDesc);

SECURITY_STATUS SEC_ENTRY
QueryContextAttributesW(
    PCtxtHandle phContext,              // Context to query
    unsigned long ulAttribute,          // Attribute to query
    void SEC_FAR * pBuffer              // Buffer for attributes
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_CONTEXT_ATTRIBUTES_FN_W)(
    PCtxtHandle,
    unsigned long,
    void SEC_FAR *);

// end_ntifs

SECURITY_STATUS SEC_ENTRY
QueryContextAttributesA(
    PCtxtHandle phContext,              // Context to query
    unsigned long ulAttribute,          // Attribute to query
    void SEC_FAR * pBuffer              // Buffer for attributes
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_CONTEXT_ATTRIBUTES_FN_A)(
    PCtxtHandle,
    unsigned long,
    void SEC_FAR *);

#ifdef UNICODE
#  define QueryContextAttributes QueryContextAttributesW            // ntifs
#  define QUERY_CONTEXT_ATTRIBUTES_FN QUERY_CONTEXT_ATTRIBUTES_FN_W // ntifs
#else
#  define QueryContextAttributes QueryContextAttributesA
#  define QUERY_CONTEXT_ATTRIBUTES_FN QUERY_CONTEXT_ATTRIBUTES_FN_A
#endif // !UNICODE

// begin_ntifs

SECURITY_STATUS SEC_ENTRY
QueryCredentialsAttributesW(
    PCredHandle phCredential,              // Credential to query
    unsigned long ulAttribute,          // Attribute to query
    void SEC_FAR * pBuffer              // Buffer for attributes
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_CREDENTIALS_ATTRIBUTES_FN_W)(
    PCredHandle,
    unsigned long,
    void SEC_FAR *);

// end_ntifs

SECURITY_STATUS SEC_ENTRY
QueryCredentialsAttributesA(
    PCredHandle phCredential,              // Credential to query
    unsigned long ulAttribute,          // Attribute to query
    void SEC_FAR * pBuffer              // Buffer for attributes
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_CREDENTIALS_ATTRIBUTES_FN_A)(
    PCredHandle,
    unsigned long,
    void SEC_FAR *);

#ifdef UNICODE
#  define QueryCredentialsAttributes QueryCredentialsAttributesW            // ntifs
#  define QUERY_CREDENTIALS_ATTRIBUTES_FN QUERY_CREDENTIALS_ATTRIBUTES_FN_W // ntifs
#else
#  define QueryCredentialsAttributes QueryCredentialsAttributesA
#  define QUERY_CREDENTIALS_ATTRIBUTES_FN QUERY_CREDENTIALS_ATTRIBUTES_FN_A
#endif // !UNICODE

// begin_ntifs

SECURITY_STATUS SEC_ENTRY
FreeContextBuffer(
    void SEC_FAR * pvContextBuffer      // buffer to free
    );

typedef SECURITY_STATUS
(SEC_ENTRY * FREE_CONTEXT_BUFFER_FN)(
    void SEC_FAR *);

// end_ntifs

// begin_ntifs
///////////////////////////////////////////////////////////////////
////
////    Message Support API
////
//////////////////////////////////////////////////////////////////

SECURITY_STATUS SEC_ENTRY
MakeSignature(
    PCtxtHandle phContext,              // Context to use
    unsigned long fQOP,                 // Quality of Protection
    PSecBufferDesc pMessage,            // Message to sign
    unsigned long MessageSeqNo          // Message Sequence Num.
    );

typedef SECURITY_STATUS
(SEC_ENTRY * MAKE_SIGNATURE_FN)(
    PCtxtHandle,
    unsigned long,
    PSecBufferDesc,
    unsigned long);

SECURITY_STATUS SEC_ENTRY
VerifySignature(
    PCtxtHandle phContext,              // Context to use
    PSecBufferDesc pMessage,            // Message to verify
    unsigned long MessageSeqNo,         // Sequence Num.
    unsigned long SEC_FAR * pfQOP       // QOP used
    );

typedef SECURITY_STATUS
(SEC_ENTRY * VERIFY_SIGNATURE_FN)(
    PCtxtHandle,
    PSecBufferDesc,
    unsigned long,
    unsigned long SEC_FAR *);

SECURITY_STATUS SEC_ENTRY
EncryptMessage( PCtxtHandle         phContext,
                unsigned long       fQOP,
                PSecBufferDesc      pMessage,
                unsigned long       MessageSeqNo);

typedef SECURITY_STATUS
(SEC_ENTRY * ENCRYPT_MESSAGE_FN)(
    PCtxtHandle, unsigned long, PSecBufferDesc, unsigned long);

SECURITY_STATUS SEC_ENTRY
DecryptMessage( PCtxtHandle         phContext,
                PSecBufferDesc      pMessage,
                unsigned long       MessageSeqNo,
                unsigned long *     pfQOP);

typedef SECURITY_STATUS
(SEC_ENTRY * DECRYPT_MESSAGE_FN)(
    PCtxtHandle, PSecBufferDesc, unsigned long,
    unsigned long SEC_FAR *);

// end_ntifs

// begin_ntifs
///////////////////////////////////////////////////////////////////////////
////
////    Misc.
////
///////////////////////////////////////////////////////////////////////////

SECURITY_STATUS SEC_ENTRY
EnumerateSecurityPackagesW(
    unsigned long SEC_FAR * pcPackages,     // Receives num. packages
    PSecPkgInfoW SEC_FAR * ppPackageInfo    // Receives array of info
    );

typedef SECURITY_STATUS
(SEC_ENTRY * ENUMERATE_SECURITY_PACKAGES_FN_W)(
    unsigned long SEC_FAR *,
    PSecPkgInfoW SEC_FAR *);

// end_ntifs

SECURITY_STATUS SEC_ENTRY
EnumerateSecurityPackagesA(
    unsigned long SEC_FAR * pcPackages,     // Receives num. packages
    PSecPkgInfoA SEC_FAR * ppPackageInfo    // Receives array of info
    );

typedef SECURITY_STATUS
(SEC_ENTRY * ENUMERATE_SECURITY_PACKAGES_FN_A)(
    unsigned long SEC_FAR *,
    PSecPkgInfoA SEC_FAR *);

#ifdef UNICODE
#  define EnumerateSecurityPackages EnumerateSecurityPackagesW              // ntifs
#  define ENUMERATE_SECURITY_PACKAGES_FN ENUMERATE_SECURITY_PACKAGES_FN_W   // ntifs
#else
#  define EnumerateSecurityPackages EnumerateSecurityPackagesA
#  define ENUMERATE_SECURITY_PACKAGES_FN ENUMERATE_SECURITY_PACKAGES_FN_A
#endif // !UNICODE

// begin_ntifs

SECURITY_STATUS SEC_ENTRY
QuerySecurityPackageInfoW(
#if ISSP_MODE == 0
    PSECURITY_STRING pPackageName,
#else
    SEC_WCHAR SEC_FAR * pszPackageName,     // Name of package
#endif
    PSecPkgInfoW SEC_FAR *ppPackageInfo              // Receives package info
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_SECURITY_PACKAGE_INFO_FN_W)(
#if ISSP_MODE == 0
    PSECURITY_STRING,
#else
    SEC_WCHAR SEC_FAR *,
#endif
    PSecPkgInfoW SEC_FAR *);

// end_ntifs

SECURITY_STATUS SEC_ENTRY
QuerySecurityPackageInfoA(
    SEC_CHAR SEC_FAR * pszPackageName,      // Name of package
    PSecPkgInfoA SEC_FAR *ppPackageInfo              // Receives package info
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_SECURITY_PACKAGE_INFO_FN_A)(
    SEC_CHAR SEC_FAR *,
    PSecPkgInfoA SEC_FAR *);

#ifdef UNICODE
#  define QuerySecurityPackageInfo QuerySecurityPackageInfoW                // ntifs
#  define QUERY_SECURITY_PACKAGE_INFO_FN QUERY_SECURITY_PACKAGE_INFO_FN_W   // ntifs
#else
#  define QuerySecurityPackageInfo QuerySecurityPackageInfoA
#  define QUERY_SECURITY_PACKAGE_INFO_FN QUERY_SECURITY_PACKAGE_INFO_FN_A
#endif // !UNICODE

#if ISSP_MODE == 0

#define DeleteSecurityContextDefer  DeleteSecurityContext
#define FreeCredentialsHandleDefer  FreeCredentialsHandle

#if 0
//
// Deferred mode calls for rdr
//

SECURITY_STATUS SEC_ENTRY
DeleteSecurityContextDefer(
    PCtxtHandle     phContext);

SECURITY_STATUS SEC_ENTRY
FreeCredentialsHandleDefer(
    PCredHandle     phCreds);

#endif

#endif

typedef enum _SecDelegationType {
    SecFull,
    SecService,
    SecTree,
    SecDirectory,
    SecObject
} SecDelegationType, * PSecDelegationType;

SECURITY_STATUS SEC_ENTRY
DelegateSecurityContext(
    PCtxtHandle         phContext,          // IN Active context to delegate
#if ISSP_MODE == 0
    PSECURITY_STRING    pTarget,            // IN Target path
#else
    SEC_CHAR SEC_FAR *  pszTarget,
#endif
    SecDelegationType   DelegationType,     // IN Type of delegation
    PTimeStamp          pExpiry,            // IN OPTIONAL time limit
    PSecBuffer          pPackageParameters, // IN OPTIONAL package specific
    PSecBufferDesc      pOutput);           // OUT Token for applycontroltoken.

///////////////////////////////////////////////////////////////////////////
////
////    Proxies
////
///////////////////////////////////////////////////////////////////////////

//
// Proxies are only available on NT platforms
//

// begin_ntifs

///////////////////////////////////////////////////////////////////////////
////
////    Context export/import
////
///////////////////////////////////////////////////////////////////////////

SECURITY_STATUS SEC_ENTRY
ExportSecurityContext(
    PCtxtHandle          phContext,             // (in) context to export
    ULONG                fFlags,                // (in) option flags
    PSecBuffer           pPackedContext,        // (out) marshalled context
    void SEC_FAR * SEC_FAR * pToken                 // (out, optional) token handle for impersonation
    );

typedef SECURITY_STATUS
(SEC_ENTRY * EXPORT_SECURITY_CONTEXT_FN)(
    PCtxtHandle,
    ULONG,
    PSecBuffer,
    void SEC_FAR * SEC_FAR *
    );

SECURITY_STATUS SEC_ENTRY
ImportSecurityContextW(
    SEC_WCHAR SEC_FAR * pszPackage,
    PSecBuffer           pPackedContext,        // (in) marshalled context
    void SEC_FAR *       Token,                 // (in, optional) handle to token for context
    PCtxtHandle          phContext              // (out) new context handle
    );

typedef SECURITY_STATUS
(SEC_ENTRY * IMPORT_SECURITY_CONTEXT_FN_W)(
    SEC_WCHAR SEC_FAR *,
    PSecBuffer,
    VOID SEC_FAR *,
    PCtxtHandle
    );

// end_ntifs
SECURITY_STATUS SEC_ENTRY
ImportSecurityContextA(
    SEC_CHAR SEC_FAR * pszPackage,
    PSecBuffer           pPackedContext,        // (in) marshalled context
    VOID SEC_FAR *       Token,                 // (in, optional) handle to token for context
    PCtxtHandle          phContext              // (out) new context handle
    );

typedef SECURITY_STATUS
(SEC_ENTRY * IMPORT_SECURITY_CONTEXT_FN_A)(
    SEC_CHAR SEC_FAR *,
    PSecBuffer,
    void SEC_FAR *,
    PCtxtHandle
    );

#ifdef UNICODE
#  define ImportSecurityContext ImportSecurityContextW              // ntifs
#  define IMPORT_SECURITY_CONTEXT_FN IMPORT_SECURITY_CONTEXT_FN_W   // ntifs
#else
#  define ImportSecurityContext ImportSecurityContextA
#  define IMPORT_SECURITY_CONTEXT_FN IMPORT_SECURITY_CONTEXT_FN_A
#endif // !UNICODE

///////////////////////////////////////////////////////////////////////////////
////
////  Fast access for RPC:
////
///////////////////////////////////////////////////////////////////////////////

#define SECURITY_ENTRYPOINT_ANSIW "InitSecurityInterfaceW"
#define SECURITY_ENTRYPOINT_ANSIA "InitSecurityInterfaceW"
#define SECURITY_ENTRYPOINTW SEC_TEXT("InitSecurityInterfaceW")     // ntifs
#define SECURITY_ENTRYPOINTA SEC_TEXT("InitSecurityInterfaceA")
#define SECURITY_ENTRYPOINT16 "INITSECURITYINTERFACEA"

#ifdef SECURITY_WIN32
#  ifdef UNICODE
#    define SECURITY_ENTRYPOINT SECURITY_ENTRYPOINTW                // ntifs
#    define SECURITY_ENTRYPOINT_ANSI SECURITY_ENTRYPOINT_ANSIW
#  else // UNICODE
#    define SECURITY_ENTRYPOINT SECURITY_ENTRYPOINTA
#    define SECURITY_ENTRYPOINT_ANSI SECURITY_ENTRYPOINT_ANSIA
#  endif // UNICODE
#else // SECURITY_WIN32
#  define SECURITY_ENTRYPOINT SECURITY_ENTRYPOINT16
#  define SECURITY_ENTRYPOINT_ANSI SECURITY_ENTRYPOINT16
#endif // SECURITY_WIN32

// begin_ntifs

typedef struct _SECURITY_FUNCTION_TABLE_W {
    unsigned long                       dwVersion;
    ENUMERATE_SECURITY_PACKAGES_FN_W    EnumerateSecurityPackagesW;
    QUERY_CREDENTIALS_ATTRIBUTES_FN_W   QueryCredentialsAttributesW;
    ACQUIRE_CREDENTIALS_HANDLE_FN_W     AcquireCredentialsHandleW;
    FREE_CREDENTIALS_HANDLE_FN          FreeCredentialHandle;
#ifndef WIN32_CHICAGO
    void SEC_FAR *                      Reserved2;
#else // WIN32_CHICAGO
    SSPI_LOGON_USER_FN                       SspiLogonUserW;
#endif // WIN32_CHICAGO
    INITIALIZE_SECURITY_CONTEXT_FN_W    InitializeSecurityContextW;
    ACCEPT_SECURITY_CONTEXT_FN          AcceptSecurityContext;
    COMPLETE_AUTH_TOKEN_FN              CompleteAuthToken;
    DELETE_SECURITY_CONTEXT_FN          DeleteSecurityContext;
    APPLY_CONTROL_TOKEN_FN              ApplyControlToken;
    QUERY_CONTEXT_ATTRIBUTES_FN_W       QueryContextAttributesW;
    IMPERSONATE_SECURITY_CONTEXT_FN     ImpersonateSecurityContext;
    REVERT_SECURITY_CONTEXT_FN          RevertSecurityContext;
    MAKE_SIGNATURE_FN                   MakeSignature;
    VERIFY_SIGNATURE_FN                 VerifySignature;
    FREE_CONTEXT_BUFFER_FN              FreeContextBuffer;
    QUERY_SECURITY_PACKAGE_INFO_FN_W    QuerySecurityPackageInfoW;
    void SEC_FAR *                      Reserved3;
    void SEC_FAR *                      Reserved4;
    EXPORT_SECURITY_CONTEXT_FN          ExportSecurityContext;
    IMPORT_SECURITY_CONTEXT_FN_W        ImportSecurityContextW;
    void SEC_FAR *                      Reserved7;
    void SEC_FAR *                      Reserved8;
    QUERY_SECURITY_CONTEXT_TOKEN_FN     QuerySecurityContextToken;
    ENCRYPT_MESSAGE_FN                  EncryptMessage;
    DECRYPT_MESSAGE_FN                  DecryptMessage;
} SecurityFunctionTableW, SEC_FAR * PSecurityFunctionTableW;

// end_ntifs

typedef struct _SECURITY_FUNCTION_TABLE_A {
    unsigned long                       dwVersion;
    ENUMERATE_SECURITY_PACKAGES_FN_A    EnumerateSecurityPackagesA;
    QUERY_CREDENTIALS_ATTRIBUTES_FN_A   QueryCredentialsAttributesA;
    ACQUIRE_CREDENTIALS_HANDLE_FN_A     AcquireCredentialsHandleA;
    FREE_CREDENTIALS_HANDLE_FN          FreeCredentialHandle;
#ifndef WIN32_CHICAGO
    void SEC_FAR *                      Reserved2;
#else // WIN32_CHICAGO
    SSPI_LOGON_USER_FN                       SspiLogonUserA;
#endif // WIN32_CHICAGO
    INITIALIZE_SECURITY_CONTEXT_FN_A    InitializeSecurityContextA;
    ACCEPT_SECURITY_CONTEXT_FN          AcceptSecurityContext;
    COMPLETE_AUTH_TOKEN_FN              CompleteAuthToken;
    DELETE_SECURITY_CONTEXT_FN          DeleteSecurityContext;
    APPLY_CONTROL_TOKEN_FN              ApplyControlToken;
    QUERY_CONTEXT_ATTRIBUTES_FN_A       QueryContextAttributesA;
    IMPERSONATE_SECURITY_CONTEXT_FN     ImpersonateSecurityContext;
    REVERT_SECURITY_CONTEXT_FN          RevertSecurityContext;
    MAKE_SIGNATURE_FN                   MakeSignature;
    VERIFY_SIGNATURE_FN                 VerifySignature;
    FREE_CONTEXT_BUFFER_FN              FreeContextBuffer;
    QUERY_SECURITY_PACKAGE_INFO_FN_A    QuerySecurityPackageInfoA;
    void SEC_FAR *                      Reserved3;
    void SEC_FAR *                      Reserved4;
    EXPORT_SECURITY_CONTEXT_FN          ExportSecurityContext;
    IMPORT_SECURITY_CONTEXT_FN_A        ImportSecurityContextA;
   void SEC_FAR *                      Reserved7;
    void SEC_FAR *                      Reserved8;
    QUERY_SECURITY_CONTEXT_TOKEN_FN     QuerySecurityContextToken;
    ENCRYPT_MESSAGE_FN                  EncryptMessage;
    DECRYPT_MESSAGE_FN                  DecryptMessage;
} SecurityFunctionTableA, SEC_FAR * PSecurityFunctionTableA;

#ifdef UNICODE
#  define SecurityFunctionTable SecurityFunctionTableW
#  define PSecurityFunctionTable PSecurityFunctionTableW
#else
#  define SecurityFunctionTable SecurityFunctionTableA
#  define PSecurityFunctionTable PSecurityFunctionTableA
#endif // !UNICODE

#define SECURITY_

#define SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION     1

PSecurityFunctionTableA SEC_ENTRY
InitSecurityInterfaceA(
    void
    );

typedef PSecurityFunctionTableA
(SEC_ENTRY * INIT_SECURITY_INTERFACE_A)(void);

// begin_ntifs

PSecurityFunctionTableW SEC_ENTRY
InitSecurityInterfaceW(
    void
    );

typedef PSecurityFunctionTableW
(SEC_ENTRY * INIT_SECURITY_INTERFACE_W)(void);

// end_ntifs

#ifdef UNICODE
#  define InitSecurityInterface InitSecurityInterfaceW          // ntifs
#  define INIT_SECURITY_INTERFACE INIT_SECURITY_INTERFACE_W     // ntifs
#else
#  define InitSecurityInterface InitSecurityInterfaceA
#  define INIT_SECURITY_INTERFACE INIT_SECURITY_INTERFACE_A
#endif // !UNICODE

typedef struct _SECURITY_PACKAGE_OPTIONS {
    unsigned long   Size;
    unsigned long   Type;
    unsigned long   Flags;
    unsigned long   SignatureSize;
    void SEC_FAR *  Signature;
} SECURITY_PACKAGE_OPTIONS, SEC_FAR * PSECURITY_PACKAGE_OPTIONS;

#define SECPKG_OPTIONS_TYPE_UNKNOWN 0
#define SECPKG_OPTIONS_TYPE_LSA     1
#define SECPKG_OPTIONS_TYPE_SSPI    2

#define SECPKG_OPTIONS_PERMANENT    0x00000001

SECURITY_STATUS
SEC_ENTRY
AddSecurityPackageA(
    SEC_CHAR SEC_FAR *  pszPackageName,
    SECURITY_PACKAGE_OPTIONS SEC_FAR * Options
    );

SECURITY_STATUS
SEC_ENTRY
AddSecurityPackageW(
    SEC_WCHAR SEC_FAR * pszPackageName,
    SECURITY_PACKAGE_OPTIONS SEC_FAR * Options
    );

#ifdef UNICODE
#define AddSecurityPackage  AddSecurityPackageW
#else
#define AddSecurityPackage  AddSecurityPackageA
#endif

SECURITY_STATUS
SEC_ENTRY
DeleteSecurityPackageA(
    SEC_CHAR SEC_FAR *  pszPackageName );

SECURITY_STATUS
SEC_ENTRY
DeleteSecurityPackageW(
    SEC_WCHAR SEC_FAR * pszPackageName );

#ifdef UNICODE
#define DeleteSecurityPackage   DeleteSecurityPackageW
#else
#define DeleteSecurityPackage   DeleteSecurityPackageA
#endif

//
// Extended Name APIs for NTDS
//

typedef enum
{
    // Examples for the following formats assume a fictitous company
    // which hooks into the global X.500 and DNS name spaces as follows.
    //
    // Enterprise root domain in DNS is
    //
    //      widget.com
    //
    // Enterprise root domain in X.500 (RFC 1779 format) is
    //
    //      O=Widget, C=US
    //
    // There exists the child domain
    //
    //      engineering.widget.com
    //
    // equivalent to
    //
    //      OU=Engineering, O=Widget, C=US
    //
    // There exists a container within the Engineering domain
    //
    //      OU=Software, OU=Engineering, O=Widget, C=US
    //
    // There exists the user
    //
    //      CN=Spencer Katt, OU=Software, OU=Engineering, O=Widget, C=US
    //
    // And this user's downlevel (pre-NTDS) user name is
    //
    //      Engineering\SpencerK

    // unknown name type
    NameUnknown = 0,

    // CN=Spencer Katt, OU=Software, OU=Engineering, O=Widget, C=US
    NameFullyQualifiedDN = 1,

    // Engineering\SpencerK
    NameSamCompatible = 2,

    // Probably "Spencer Katt" but could be something else.  I.e. The
    // display name is not necessarily the defining RDN.
    NameDisplay = 3,

    // xxx@engineering.widget.com where xxx could be "SpencerK" or
    // anything else.  Could be multi-valued to handle migration and aliasing.
    NameDomainSimple = 4,

    // xxx@widget.com where xxx could be "SpencerK" or anything else.
    // Could be multi-valued to handle migration and aliasing.
    NameEnterpriseSimple = 5,

    // String-ized GUID as returned by IIDFromString().
    // eg: {4fa050f0-f561-11cf-bdd9-00aa003a77b6}
    NameUniqueId = 6,

    // engineering.widget.com/software/spencer katt
    NameCanonical = 7

} EXTENDED_NAME_FORMAT, * PEXTENDED_NAME_FORMAT ;

BOOLEAN
SEC_ENTRY
GetUserNameExA(
    EXTENDED_NAME_FORMAT  NameFormat,
    LPSTR lpNameBuffer,
    PULONG nSize
    );
BOOLEAN
SEC_ENTRY
GetUserNameExW(
    EXTENDED_NAME_FORMAT NameFormat,
    LPWSTR lpNameBuffer,
    PULONG nSize
    );

#ifdef UNICODE
#define GetUserNameEx   GetUserNameExW
#else
#define GetUserNameEx   GetUserNameExA
#endif

BOOLEAN
SEC_ENTRY
TranslateNameA(
    LPCSTR lpAccountName,
    EXTENDED_NAME_FORMAT AccountNameFormat,
    EXTENDED_NAME_FORMAT DesiredNameFormat,
    LPSTR lpTranslatedName,
    PULONG nSize
    );
BOOLEAN
SEC_ENTRY
TranslateNameW(
    LPCWSTR lpAccountName,
    EXTENDED_NAME_FORMAT AccountNameFormat,
    EXTENDED_NAME_FORMAT DesiredNameFormat,
    LPWSTR lpTranslatedName,
    PULONG nSize
    );
#ifdef UNICODE
#define TranslateName   TranslateNameW
#else
#define TranslateName   TranslateNameA
#endif

#ifdef SECURITY_DOS
#pragma warning(default:4147)
#endif

#endif // __SSPI_H__


SUBAUTH.H

/*++ BUILD Version: 0005     Increment this if a change has global effects

Copyright (c) 1990-1997  Microsoft Corporation

Module Name:

    subauth.h

Abstract:

    This module defines types and macros for Subauthentication Packages.

Revision History:

--*/

#ifndef _NTSUBAUTH_
#define _NTSUBAUTH_

#ifdef __cplusplus
extern "C" {
#endif

#ifndef _NTDEF_
typedef LONG NTSTATUS, *PNTSTATUS;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _STRING {
    USHORT Length;
    USHORT MaximumLength;
    PCHAR Buffer;
} STRING, *PSTRING;

typedef LARGE_INTEGER OLD_LARGE_INTEGER;
typedef LARGE_INTEGER POLD_LARGE_INTEGER;

#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)

#endif

typedef PVOID SAM_HANDLE, *PSAM_HANDLE;

//
// User account control flags...
//

#define USER_ACCOUNT_DISABLED          (0x00000001)
#define USER_HOME_DIRECTORY_REQUIRED   (0x00000002)
#define USER_PASSWORD_NOT_REQUIRED     (0x00000004)
#define USER_TEMP_DUPLICATE_ACCOUNT    (0x00000008)
#define USER_NORMAL_ACCOUNT            (0x00000010)
#define USER_MNS_LOGON_ACCOUNT         (0x00000020)
#define USER_INTERDOMAIN_TRUST_ACCOUNT (0x00000040)
#define USER_WORKSTATION_TRUST_ACCOUNT (0x00000080)
#define USER_SERVER_TRUST_ACCOUNT      (0x00000100)
#define USER_DONT_EXPIRE_PASSWORD      (0x00000200)
#define USER_ACCOUNT_AUTO_LOCKED       (0x00000400)

#define USER_MACHINE_ACCOUNT_MASK      \
            ( USER_INTERDOMAIN_TRUST_ACCOUNT |\
              USER_WORKSTATION_TRUST_ACCOUNT |\
              USER_SERVER_TRUST_ACCOUNT)

#define USER_ACCOUNT_TYPE_MASK         \
            ( USER_TEMP_DUPLICATE_ACCOUNT |\
              USER_NORMAL_ACCOUNT |\
              USER_MACHINE_ACCOUNT_MASK )

//
// Logon times may be expressed in day, hour, or minute granularity.
//
//              Days per week    = 7
//              Hours per week   = 168
//              Minutes per week = 10080
//

#define SAM_DAYS_PER_WEEK    (7)
#define SAM_HOURS_PER_WEEK   (24 * SAM_DAYS_PER_WEEK)
#define SAM_MINUTES_PER_WEEK (60 * SAM_HOURS_PER_WEEK)

typedef struct _LOGON_HOURS {

    USHORT UnitsPerWeek;

    //
    // UnitsPerWeek is the number of equal length time units the week is
    // divided into.  This value is used to compute the length of the bit
    // string in logon_hours.  Must be less than or equal to
    // SAM_UNITS_PER_WEEK (10080) for this release.
    //
    // LogonHours is a bit map of valid logon times.  Each bit represents
    // a unique division in a week.  The largest bit map supported is 1260
    // bytes (10080 bits), which represents minutes per week.  In this case
    // the first bit (bit 0, byte 0) is Sunday, 00:00:00 - 00-00:59; bit 1,
    // byte 0 is Sunday, 00:01:00 - 00:01:59, etc.  A NULL pointer means
    // DONT_CHANGE for SamSetInformationUser() calls.
    //

    PUCHAR LogonHours;

} LOGON_HOURS, *PLOGON_HOURS;

typedef struct _SR_SECURITY_DESCRIPTOR {
    ULONG Length;
    PUCHAR SecurityDescriptor;
} SR_SECURITY_DESCRIPTOR, *PSR_SECURITY_DESCRIPTOR;

#include "pshpack4.h"
typedef struct _USER_ALL_INFORMATION {
    LARGE_INTEGER LastLogon;
    LARGE_INTEGER LastLogoff;
    LARGE_INTEGER PasswordLastSet;
    LARGE_INTEGER AccountExpires;
    LARGE_INTEGER PasswordCanChange;
    LARGE_INTEGER PasswordMustChange;
    UNICODE_STRING UserName;
    UNICODE_STRING FullName;
    UNICODE_STRING HomeDirectory;
    UNICODE_STRING HomeDirectoryDrive;
    UNICODE_STRING ScriptPath;
    UNICODE_STRING ProfilePath;
    UNICODE_STRING AdminComment;
    UNICODE_STRING WorkStations;
    UNICODE_STRING UserComment;
    UNICODE_STRING Parameters;
    UNICODE_STRING LmPassword;
    UNICODE_STRING NtPassword;
    UNICODE_STRING PrivateData;
    SR_SECURITY_DESCRIPTOR SecurityDescriptor;
    ULONG UserId;
    ULONG PrimaryGroupId;
    ULONG UserAccountControl;
    ULONG WhichFields;
    LOGON_HOURS LogonHours;
    USHORT BadPasswordCount;
    USHORT LogonCount;
    USHORT CountryCode;
    USHORT CodePage;
    BOOLEAN LmPasswordPresent;
    BOOLEAN NtPasswordPresent;
    BOOLEAN PasswordExpired;
    BOOLEAN PrivateDataSensitive;
} USER_ALL_INFORMATION,  *PUSER_ALL_INFORMATION;
#include "poppack.h"
#define USER_ALL_PARAMETERS         0x00200000

#define CLEAR_BLOCK_LENGTH          8

typedef struct _CLEAR_BLOCK {
    CHAR    data[CLEAR_BLOCK_LENGTH];
}                                   CLEAR_BLOCK;
typedef CLEAR_BLOCK *               PCLEAR_BLOCK;

#define CYPHER_BLOCK_LENGTH         8

typedef struct _CYPHER_BLOCK {
    CHAR    data[CYPHER_BLOCK_LENGTH];
}                                   CYPHER_BLOCK;
typedef CYPHER_BLOCK *              PCYPHER_BLOCK;
typedef struct _LM_OWF_PASSWORD {
    CYPHER_BLOCK data[2];
}                                   LM_OWF_PASSWORD;
typedef LM_OWF_PASSWORD *           PLM_OWF_PASSWORD;
typedef CLEAR_BLOCK                 LM_CHALLENGE;
typedef LM_CHALLENGE *              PLM_CHALLENGE;
typedef LM_OWF_PASSWORD             NT_OWF_PASSWORD;
typedef NT_OWF_PASSWORD *           PNT_OWF_PASSWORD;
typedef LM_CHALLENGE                NT_CHALLENGE;
typedef NT_CHALLENGE *              PNT_CHALLENGE;
#define USER_SESSION_KEY_LENGTH     (CYPHER_BLOCK_LENGTH * 2)

typedef struct _USER_SESSION_KEY {
    CYPHER_BLOCK data[2];
}                                   USER_SESSION_KEY;
typedef USER_SESSION_KEY          * PUSER_SESSION_KEY;

typedef enum _NETLOGON_LOGON_INFO_CLASS {
    NetlogonInteractiveInformation = 1,
    NetlogonNetworkInformation,
    NetlogonServiceInformation,
    NetlogonGenericInformation
} NETLOGON_LOGON_INFO_CLASS;

#pragma pack(4)
typedef struct _NETLOGON_LOGON_IDENTITY_INFO {
    UNICODE_STRING LogonDomainName;
    ULONG ParameterControl;
    OLD_LARGE_INTEGER  LogonId;
    UNICODE_STRING UserName;
    UNICODE_STRING Workstation;
} NETLOGON_LOGON_IDENTITY_INFO,
 *PNETLOGON_LOGON_IDENTITY_INFO;
#pragma pack()

#pragma pack(4)
typedef struct _NETLOGON_INTERACTIVE_INFO {
    NETLOGON_LOGON_IDENTITY_INFO Identity;
    LM_OWF_PASSWORD LmOwfPassword;
    NT_OWF_PASSWORD NtOwfPassword;
} NETLOGON_INTERACTIVE_INFO,
 *PNETLOGON_INTERACTIVE_INFO;
#pragma pack()

#pragma pack(4)
typedef struct _NETLOGON_SERVICE_INFO {
    NETLOGON_LOGON_IDENTITY_INFO Identity;
    LM_OWF_PASSWORD LmOwfPassword;
    NT_OWF_PASSWORD NtOwfPassword;
} NETLOGON_SERVICE_INFO, *PNETLOGON_SERVICE_INFO;
#pragma pack()

#pragma pack(4)
typedef struct _NETLOGON_NETWORK_INFO {
    NETLOGON_LOGON_IDENTITY_INFO Identity;
    LM_CHALLENGE LmChallenge;
    STRING NtChallengeResponse;
    STRING LmChallengeResponse;
} NETLOGON_NETWORK_INFO, *PNETLOGON_NETWORK_INFO;
#pragma pack()

#pragma pack(4)
typedef struct _NETLOGON_GENERIC_INFO {
    NETLOGON_LOGON_IDENTITY_INFO Identity;
    UNICODE_STRING PackageName;
    ULONG DataLength;
#ifdef MIDL_PASS
    [size_is(DataLength)]
#endif
    PUCHAR LogonData;
} NETLOGON_GENERIC_INFO, *PNETLOGON_GENERIC_INFO;
#pragma pack()

// Values for Flags
#define MSV1_0_PASSTHRU     0x01
#define MSV1_0_GUEST_LOGON  0x02

NTSTATUS NTAPI
Msv1_0SubAuthenticationRoutine(
    IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
    IN PVOID LogonInformation,
    IN ULONG Flags,
    IN PUSER_ALL_INFORMATION UserAll,
    OUT PULONG WhichFields,
    OUT PULONG UserFlags,
    OUT PBOOLEAN Authoritative,
    OUT PLARGE_INTEGER LogoffTime,
    OUT PLARGE_INTEGER KickoffTime
);

typedef struct _MSV1_0_VALIDATION_INFO {
    LARGE_INTEGER LogoffTime;
    LARGE_INTEGER KickoffTime;
    UNICODE_STRING LogonServer;
    UNICODE_STRING LogonDomainName;
    USER_SESSION_KEY SessionKey;
    BOOLEAN Authoritative;
    ULONG UserFlags;
    ULONG WhichFields;
    ULONG UserId;
} MSV1_0_VALIDATION_INFO, *PMSV1_0_VALIDATION_INFO;

// values for WhichFields

#define MSV1_0_VALIDATION_LOGOFF_TIME          0x00000001
#define MSV1_0_VALIDATION_KICKOFF_TIME         0x00000002
#define MSV1_0_VALIDATION_LOGON_SERVER         0x00000004
#define MSV1_0_VALIDATION_LOGON_DOMAIN         0x00000008
#define MSV1_0_VALIDATION_SESSION_KEY          0x00000010
#define MSV1_0_VALIDATION_USER_FLAGS           0x00000020
#define MSV1_0_VALIDATION_USER_ID              0x00000040

// legal values for ActionsPerformed
#define MSV1_0_SUBAUTH_ACCOUNT_DISABLED        0x00000001
#define MSV1_0_SUBAUTH_PASSWORD                0x00000002
#define MSV1_0_SUBAUTH_WORKSTATIONS            0x00000004
#define MSV1_0_SUBAUTH_LOGON_HOURS             0x00000008
#define MSV1_0_SUBAUTH_ACCOUNT_EXPIRY          0x00000010
#define MSV1_0_SUBAUTH_PASSWORD_EXPIRY         0x00000020
#define MSV1_0_SUBAUTH_ACCOUNT_TYPE            0x00000040
#define MSV1_0_SUBAUTH_LOCKOUT                 0x00000080

NTSTATUS NTAPI
Msv1_0SubAuthenticationRoutineEx(
    IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
    IN PVOID LogonInformation,
    IN ULONG Flags,
    IN PUSER_ALL_INFORMATION UserAll,
    IN SAM_HANDLE UserHandle,
    IN OUT PMSV1_0_VALIDATION_INFO ValidationInfo,
    OUT PULONG ActionsPerformed
);

NTSTATUS NTAPI
Msv1_0SubAuthenticationRoutineGeneric(
    IN PVOID SubmitBuffer,
    IN ULONG SubmitBufferLength,
    OUT PULONG ReturnBufferLength,
    OUT PVOID *ReturnBuffer
);

NTSTATUS NTAPI
Msv1_0SubAuthenticationFilter(
    IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
    IN PVOID LogonInformation,
    IN ULONG Flags,
    IN PUSER_ALL_INFORMATION UserAll,
    OUT PULONG WhichFields,
    OUT PULONG UserFlags,
    OUT PBOOLEAN Authoritative,
    OUT PLARGE_INTEGER LogoffTime,
    OUT PLARGE_INTEGER KickoffTime
);

#define STATUS_SUCCESS                          ((NTSTATUS)0x00000000L)
#define STATUS_INVALID_INFO_CLASS        ((NTSTATUS)0xC0000003L)
#define STATUS_ACCESS_VIOLATION          ((NTSTATUS)0xC0000005L)
#define STATUS_NO_SUCH_USER              ((NTSTATUS)0xC0000064L)
#define STATUS_WRONG_PASSWORD            ((NTSTATUS)0xC000006AL)
#define STATUS_PASSWORD_RESTRICTION      ((NTSTATUS)0xC000006CL)
#define STATUS_LOGON_FAILURE             ((NTSTATUS)0xC000006DL)
#define STATUS_ACCOUNT_RESTRICTION       ((NTSTATUS)0xC000006EL)
#define STATUS_INVALID_LOGON_HOURS       ((NTSTATUS)0xC000006FL)
#define STATUS_INVALID_WORKSTATION       ((NTSTATUS)0xC0000070L)
#define STATUS_PASSWORD_EXPIRED          ((NTSTATUS)0xC0000071L)
#define STATUS_ACCOUNT_DISABLED          ((NTSTATUS)0xC0000072L)
#define STATUS_INSUFFICIENT_RESOURCES    ((NTSTATUS)0xC000009AL)
#define STATUS_ACCOUNT_EXPIRED           ((NTSTATUS)0xC0000193L)
#define STATUS_PASSWORD_MUST_CHANGE      ((NTSTATUS)0xC0000224L)
#define STATUS_ACCOUNT_LOCKED_OUT        ((NTSTATUS)0xC0000234L)
#ifdef __cplusplus
}
#endif

#endif /* _NTSUBAUTH_ */


LSAPRIVS.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    privs.c

Abstract:

    This module illustrates how to use the Windows NT LSA security API
    to manage account privileges on the local or a remote machine.

    When targetting a domain controller for privilege update operations,
    be sure to target the primary domain controller for the domain.
    The privilege settings are replicated by the primary domain controller
    to each backup domain controller as appropriate.  The NetGetDCName()
    Lan Manager API call can be used to get the primary domain controller
    computer name from a domain name.

    For a list of privilges, consult winnt.h, and search for
    SE_ASSIGNPRIMARYTOKEN_NAME.

    For a list of logon rights, which can also be assigned using this
    sample code, consult ntsecapi.h, and search for SE_BATCH_LOGON_NAME

    You can use domain\account as argv[1]. For instance, mydomain\scott will
    grant the privilege to the mydomain domain account scott.

    The optional target machine is specified as argv[2], otherwise, the
    account database is updated on the local machine.

    The LSA APIs used by this sample are Unicode only.

    Use LsaRemoveAccountRights() to remove account rights.

Author:

    Scott Field (sfield)    17-Apr-96
        Minor cleanup

    Scott Field (sfield)    12-Jul-95

--*/

#ifndef UNICODE
#define UNICODE
#endif // UNICODE

#include <windows.h>
#include <stdio.h>

#include "ntsecapi.h"

NTSTATUS
OpenPolicy(
    LPWSTR ServerName,          // machine to open policy on (Unicode)
    DWORD DesiredAccess,        // desired access to policy
    PLSA_HANDLE PolicyHandle    // resultant policy handle
    );

BOOL
GetAccountSid(
    LPTSTR SystemName,          // where to lookup account
    LPTSTR AccountName,         // account of interest
    PSID *Sid                   // resultant buffer containing SID
    );

NTSTATUS
SetPrivilegeOnAccount(
    LSA_HANDLE PolicyHandle,    // open policy handle
    PSID AccountSid,            // SID to grant privilege to
    LPWSTR PrivilegeName,       // privilege to grant (Unicode)
    BOOL bEnable                // enable or disable
    );

void
InitLsaString(
    PLSA_UNICODE_STRING LsaString, // destination
    LPWSTR String                  // source (Unicode)
    );

void
DisplayNtStatus(
    LPSTR szAPI,                // pointer to function name (ANSI)
    NTSTATUS Status             // NTSTATUS error value
    );

void
DisplayWinError(
    LPSTR szAPI,                // pointer to function name (ANSI)
    DWORD WinError              // DWORD WinError
    );

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

//
// If you have the ddk, include ntstatus.h.
//
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS  ((NTSTATUS)0x00000000L)
#endif

int
__cdecl
main(
    int argc,
    char *argv[]
    )
{
    LSA_HANDLE PolicyHandle;

    WCHAR wComputerName[256]=L"";   // static machine name buffer
    TCHAR AccountName[256];         // static account name buffer
    PSID pSid;

    NTSTATUS Status;
    int iRetVal=RTN_ERROR;          // assume error from main

    if(argc == 1) {
        fprintf(stderr,"Usage: %s <Account> [TargetMachine]\n", argv[0]);
        return RTN_USAGE;
    }

    //
    // Pick up account name on argv[1].
    // Assumes source is ANSI. Resultant string is ANSI or Unicode
    //
    wsprintf(AccountName, TEXT("%hS"), argv[1]);

    //
    // Pick up machine name on argv[2], if appropriate
    // assumes source is ANSI. Resultant string is Unicode.
    //
    if(argc == 3) wsprintfW(wComputerName, L"%hS", argv[2]);

    //
    // Open the policy on the target machine.
    //
    Status = OpenPolicy(
                wComputerName,      // target machine
                POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES,
                &PolicyHandle       // resultant policy handle
                );

    if(Status != STATUS_SUCCESS) {
        DisplayNtStatus("OpenPolicy", Status);
        return RTN_ERROR;
    }

    //
    // Obtain the SID of the user/group.
    // Note that we could target a specific machine, but we don't.
    // Specifying NULL for target machine searches for the SID in the
    // following order: well-known, Built-in and local, primary domain,
    // trusted domains.
    //
    if(GetAccountSid(
            NULL,       // default lookup logic
            AccountName,// account to obtain SID
            &pSid       // buffer to allocate to contain resultant SID
            )) {
        //
        // We only grant the privilege if we succeeded in obtaining the
        // SID. We can actually add SIDs which cannot be looked up, but
        // looking up the SID is a good sanity check which is suitable for
        // most cases.

        //
        // Grant the SeServiceLogonRight to users represented by pSid.
        //
        Status = SetPrivilegeOnAccount(
                    PolicyHandle,           // policy handle
                    pSid,                   // SID to grant privilege
                    L"SeServiceLogonRight", // Unicode privilege
                    TRUE                    // enable the privilege
                    );

        if(Status == STATUS_SUCCESS)
            iRetVal = RTN_OK;
        else
            DisplayNtStatus("SetPrivilegeOnAccount", Status);
    }
    else {
        //
        // Error obtaining SID.
        //
        DisplayWinError("GetAccountSid", GetLastError());
    }

    //
    // Close the policy handle.
    //
    LsaClose(PolicyHandle);

    //
    // Free memory allocated for SID.
    //
    if(pSid != NULL) HeapFree(GetProcessHeap(), 0, pSid);

    return iRetVal;
}

/*++
This function attempts to obtain a SID representing the supplied
account on the supplied system.

If the function succeeds, the return value is TRUE. A buffer is
allocated which contains the SID representing the supplied account.
This buffer should be freed when it is no longer needed by calling
HeapFree(GetProcessHeap(), 0, buffer)

If the function fails, the return value is FALSE. Call GetLastError()
to obtain extended error information.

Scott Field (sfield)    12-Jul-95
--*/

BOOL
GetAccountSid(
    LPTSTR SystemName,
    LPTSTR AccountName,
    PSID *Sid
    )
{
    LPTSTR ReferencedDomain=NULL;
    DWORD cbSid=128;    // initial allocation attempt
    DWORD cchReferencedDomain=16; // initial allocation size
    SID_NAME_USE peUse;
    BOOL bSuccess=FALSE; // assume this function will fail

    __try {

    //
    // initial memory allocations
    //
    *Sid = (PSID)HeapAlloc(GetProcessHeap(), 0, cbSid);

    if(*Sid == NULL) __leave;

    ReferencedDomain = (LPTSTR)HeapAlloc(
                    GetProcessHeap(),
                    0,
                    cchReferencedDomain * sizeof(TCHAR)
                    );

    if(ReferencedDomain == NULL) __leave;

    //
    // Obtain the SID of the specified account on the specified system.
    //
    while(!LookupAccountName(
                    SystemName,         // machine to lookup account on
                    AccountName,        // account to lookup
                    *Sid,               // SID of interest
                    &cbSid,             // size of SID
                    ReferencedDomain,   // domain account was found on
                    &cchReferencedDomain,
                    &peUse
                    )) {
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
            //
            // reallocate memory
            //
            *Sid = (PSID)HeapReAlloc(
                        GetProcessHeap(),
                        0,
                        *Sid,
                        cbSid
                        );
            if(*Sid == NULL) __leave;

            ReferencedDomain = (LPTSTR)HeapReAlloc(
                        GetProcessHeap(),
                        0,
                        ReferencedDomain,
                        cchReferencedDomain * sizeof(TCHAR)
                        );
            if(ReferencedDomain == NULL) __leave;
        }
        else __leave;
    }

    //
    // Indicate success.
    //
    bSuccess = TRUE;

    } // try
    __finally {

    //
    // Cleanup and indicate failure, if appropriate.
    //

    HeapFree(GetProcessHeap(), 0, ReferencedDomain);

    if(!bSuccess) {
        if(*Sid != NULL) {
            HeapFree(GetProcessHeap(), 0, *Sid);
            *Sid = NULL;
        }
    }

    } // finally

    return bSuccess;
}

NTSTATUS
SetPrivilegeOnAccount(
    LSA_HANDLE PolicyHandle,    // open policy handle
    PSID AccountSid,            // SID to grant privilege to
    LPWSTR PrivilegeName,       // privilege to grant (Unicode)
    BOOL bEnable                // enable or disable
    )
{
    LSA_UNICODE_STRING PrivilegeString;

    //
    // Create a LSA_UNICODE_STRING for the privilege name.
    //
    InitLsaString(&PrivilegeString, PrivilegeName);

    //
    // grant or revoke the privilege, accordingly
    //
    if(bEnable) {
        return LsaAddAccountRights(
                PolicyHandle,       // open policy handle
                AccountSid,         // target SID
                &PrivilegeString,   // privileges
                1                   // privilege count
                );
    }
    else {
        return LsaRemoveAccountRights(
                PolicyHandle,       // open policy handle
                AccountSid,         // target SID
                FALSE,              // do not disable all rights
                &PrivilegeString,   // privileges
                1                   // privilege count
                );
    }
}

void
InitLsaString(
    PLSA_UNICODE_STRING LsaString,
    LPWSTR String
    )
{
    DWORD StringLength;

    if(String == NULL) {
        LsaString->Buffer = NULL;
        LsaString->Length = 0;
        LsaString->MaximumLength = 0;
        return;
    }

    StringLength = lstrlenW(String);
    LsaString->Buffer = String;
    LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
    LsaString->MaximumLength=(USHORT)(StringLength+1) * sizeof(WCHAR);
}

NTSTATUS
OpenPolicy(
    LPWSTR ServerName,
    DWORD DesiredAccess,
    PLSA_HANDLE PolicyHandle
    )
{
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    LSA_UNICODE_STRING ServerString;
    PLSA_UNICODE_STRING Server;

    //
    // Always initialize the object attributes to all zeroes.
    //
    ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

    if (ServerName != NULL) {
        //
        // Make a LSA_UNICODE_STRING out of the LPWSTR passed in
        //
        InitLsaString(&ServerString, ServerName);
        Server = &ServerString;
    } else {
        Server = NULL;
    }

    //
    // Attempt to open the policy.
    //
    return LsaOpenPolicy(
                Server,
                &ObjectAttributes,
                DesiredAccess,
                PolicyHandle
                );
}

void
DisplayNtStatus(
    LPSTR szAPI,
    NTSTATUS Status
    )
{
    //
    // Convert the NTSTATUS to Winerror. Then call DisplayWinError().
    //
    DisplayWinError(szAPI, LsaNtStatusToWinError(Status));
}

void
DisplayWinError(
    LPSTR szAPI,
    DWORD WinError
    )
{
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    //
    // TODO: Get this fprintf out of here!
    //
    fprintf(stderr,"%s error!\n", szAPI);

    if(dwBufferLength=FormatMessageA(
                        FORMAT_MESSAGE_ALLOCATE_BUFFER |
                        FORMAT_MESSAGE_FROM_SYSTEM,
                        NULL,
                        WinError,
                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                        (LPSTR) &MessageBuffer,
                        0,
                        NULL
                        ))
    {
        DWORD dwBytesWritten; // unused

        //
        // Output message string on stderr.
        //
        WriteFile(
            GetStdHandle(STD_ERROR_HANDLE),
            MessageBuffer,
            dwBufferLength,
            &dwBytesWritten,
            NULL
            );

        //
        // Free the buffer allocated by the system.
        //
        LocalFree(MessageBuffer);
    }
}


README.TXT

The APIs found in this directory are not Win32 APIs.  They are
specific APIs for Security Providers and Account Management.
Please be aware In the future many of these APIs will be replaced
with OLE Interfaces, and will subsequently be dropped.

Documentation on ntsecapi.h can be found in LSAAPI.HLP.

Documentation on sspi.h can be found in the Service Provider Documentation,
i.e.  SPK.MVB (installed by SDK setup).


LSASAMP.C

/*++

Managing user privileges can be achieved programmatically using the
following steps:

1. Open the policy on the target machine with LsaOpenPolicy(). To grant
   privileges, open the policy with POLICY_CREATE_ACCOUNT and
   POLICY_LOOKUP_NAMES access. To revoke privileges, open the policy with
   POLICY_LOOKUP_NAMES access.

2. Obtain a SID (security identifier) representing the user/group of
   interest. The LookupAccountName() and LsaLookupNames() APIs can obtain a
   SID from an account name.

3. Call LsaAddAccountRights() to grant privileges to the user(s)
   represented by the supplied SID.

4. Call LsaRemoveAccountRights() to revoke privileges from the user(s)
   represented by the supplied SID.

5. Close the policy with LsaClose().

To successfully grant and revoke privileges, the caller needs to be an
administrator on the target system.

The LSA API LsaEnumerateAccountRights() can be used to determine which
privileges have been granted to an account.

The LSA API LsaEnumerateAccountsWithUserRight() can be used to determine
which accounts have been granted a specified privilege.

Documentation and header files for these LSA APIs is provided in the
Windows 32 SDK in the MSTOOLS\SECURITY directory.

NOTE: These LSA APIs are currently implemented as Unicode only.

This sample will grant the privilege SeServiceLogonRight to the account
specified on argv[1].

This sample is dependant on these import libs

   advapi32.lib (for LsaXxx)
   user32.lib (for wsprintf)

This sample will work properly compiled ANSI or Unicode.

You can use domain\account as argv[1]. For instance, mydomain\scott will
grant the privilege to the mydomain domain account scott.

The optional target machine is specified as argv[2], otherwise, the
account database is updated on the local machine.

The LSA APIs used by this sample are Unicode only.

Use LsaRemoveAccountRights() to remove account rights.

Scott Field (sfield)    12-Jul-95

--*/

#ifndef UNICODE
#define UNICODE
#endif // UNICODE

#include <windows.h>
#include <stdio.h>

#include "ntsecapi.h"

NTSTATUS
OpenPolicy(
    LPWSTR ServerName,          // machine to open policy on (Unicode)
    DWORD DesiredAccess,        // desired access to policy
    PLSA_HANDLE PolicyHandle    // resultant policy handle
    );

BOOL
GetAccountSid(
    LPTSTR SystemName,          // where to lookup account
    LPTSTR AccountName,         // account of interest
    PSID *Sid                   // resultant buffer containing SID
    );

NTSTATUS
SetPrivilegeOnAccount(
    LSA_HANDLE PolicyHandle,    // open policy handle
    PSID AccountSid,            // SID to grant privilege to
    LPWSTR PrivilegeName,       // privilege to grant (Unicode)
    BOOL bEnable                // enable or disable
    );

void
InitLsaString(
    PLSA_UNICODE_STRING LsaString, // destination
    LPWSTR String                  // source (Unicode)
    );

void
DisplayNtStatus(
    LPSTR szAPI,                // pointer to function name (ANSI)
    NTSTATUS Status             // NTSTATUS error value
    );

void
DisplayWinError(
    LPSTR szAPI,                // pointer to function name (ANSI)
    DWORD WinError              // DWORD WinError
    );

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

//
// If you have the ddk, include ntstatus.h.
//
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS  ((NTSTATUS)0x00000000L)
#endif

int _cdecl
main(int argc, char *argv[])
{
    LSA_HANDLE PolicyHandle;
    WCHAR wComputerName[256]=L"";   // static machine name buffer
    TCHAR AccountName[256];         // static account name buffer
    PSID pSid;
    NTSTATUS Status;
    int iRetVal=RTN_ERROR;          // assume error from main

    if(argc == 1)
    {
        fprintf(stderr,"Usage: %s <Account> [TargetMachine]\n",
            argv[0]);
        return RTN_USAGE;
    }

    //
    // Pick up account name on argv[1].
    // Assumes source is ANSI. Resultant string is ANSI or Unicode
    //
    wsprintf(AccountName, TEXT("%hS"), argv[1]);

    //
    // Pick up machine name on argv[2], if appropriate
    // assumes source is ANSI. Resultant string is Unicode.
    //
    if(argc == 3) wsprintfW(wComputerName, L"%hS", argv[2]);

    //
    // Open the policy on the target machine.
    //
    if((Status=OpenPolicy(
                wComputerName,      // target machine
                POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES,
                &PolicyHandle       // resultant policy handle
                )) != STATUS_SUCCESS) {
        DisplayNtStatus("OpenPolicy", Status);
        return RTN_ERROR;
    }

    //
    // Obtain the SID of the user/group.
    // Note that we could target a specific machine, but we don't.
    // Specifying NULL for target machine searches for the SID in the
    // following order: well-known, Built-in and local, primary domain,
    // trusted domains.
    //
    if(GetAccountSid(
            NULL,       // default lookup logic
            AccountName,// account to obtain SID
            &pSid       // buffer to allocate to contain resultant SID
            )) {
        //
        // We only grant the privilege if we succeeded in obtaining the
        // SID. We can actually add SIDs which cannot be looked up, but
        // looking up the SID is a good sanity check which is suitable for
        // most cases.

        //
        // Grant the SeServiceLogonRight to users represented by pSid.
        //
        if((Status=SetPrivilegeOnAccount(
                    PolicyHandle,           // policy handle
                    pSid,                   // SID to grant privilege
                    L"SeServiceLogonRight", // Unicode privilege
                    TRUE                    // enable the privilege
                    )) == STATUS_SUCCESS)
            iRetVal=RTN_OK;
        else
            DisplayNtStatus("AddUserRightToAccount", Status);
    }
    else {
        //
        // Error obtaining SID.
        //
        DisplayWinError("GetAccountSid", GetLastError());
    }

    //
    // Close the policy handle.
    //
    LsaClose(PolicyHandle);

    //
    // Free memory allocated for SID.
    //
    if(pSid != NULL) HeapFree(GetProcessHeap(), 0, pSid);

    return iRetVal;
}

void
InitLsaString(
    PLSA_UNICODE_STRING LsaString,
    LPWSTR String
    )
{
    DWORD StringLength;

    if (String == NULL) {
        LsaString->Buffer = NULL;
        LsaString->Length = 0;
        LsaString->MaximumLength = 0;
        return;
    }

    StringLength = wcslen(String);
    LsaString->Buffer = String;
    LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
    LsaString->MaximumLength=(USHORT)(StringLength+1) * sizeof(WCHAR);
}

NTSTATUS
OpenPolicy(
    LPWSTR ServerName,
    DWORD DesiredAccess,
    PLSA_HANDLE PolicyHandle
    )
{
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    LSA_UNICODE_STRING ServerString;
    PLSA_UNICODE_STRING Server = NULL;

    //
    // Always initialize the object attributes to all zeroes.
    //
    ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

    if (ServerName != NULL) {
        //
        // Make a LSA_UNICODE_STRING out of the LPWSTR passed in
        //
        InitLsaString(&ServerString, ServerName);
        Server = &ServerString;
    }

    //
    // Attempt to open the policy.
    //
    return LsaOpenPolicy(
                Server,
                &ObjectAttributes,
                DesiredAccess,
                PolicyHandle
                );
}

/*++
This function attempts to obtain a SID representing the supplied
account on the supplied system.

If the function succeeds, the return value is TRUE. A buffer is
allocated which contains the SID representing the supplied account.
This buffer should be freed when it is no longer needed by calling
HeapFree(GetProcessHeap(), 0, buffer)

If the function fails, the return value is FALSE. Call GetLastError()
to obtain extended error information.

Scott Field (sfield)    12-Jul-95
--*/

BOOL
GetAccountSid(
    LPTSTR SystemName,
    LPTSTR AccountName,
    PSID *Sid
    )
{
    LPTSTR ReferencedDomain=NULL;
    DWORD cbSid=128;    // initial allocation attempt
    DWORD cbReferencedDomain=16; // initial allocation size
    SID_NAME_USE peUse;
    BOOL bSuccess=FALSE; // assume this function will fail

    __try {

    //
    // initial memory allocations
    //
    if((*Sid=HeapAlloc(
                    GetProcessHeap(),
                    0,
                    cbSid
                    )) == NULL) __leave;

    if((ReferencedDomain=HeapAlloc(
                    GetProcessHeap(),
                    0,
                    cbReferencedDomain
                    )) == NULL) __leave;

    //
    // Obtain the SID of the specified account on the specified system.
    //
    while(!LookupAccountName(
                    SystemName,         // machine to lookup account on
                    AccountName,        // account to lookup
                    *Sid,               // SID of interest
                    &cbSid,             // size of SID
                    ReferencedDomain,   // domain account was found on
                    &cbReferencedDomain,
                    &peUse
                    )) {
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
            //
            // reallocate memory
            //
            if((*Sid=HeapReAlloc(
                        GetProcessHeap(),
                        0,
                        *Sid,
                        cbSid
                        )) == NULL) __leave;

            if((ReferencedDomain=HeapReAlloc(
                        GetProcessHeap(),
                        0,
                        ReferencedDomain,
                        cbReferencedDomain
                        )) == NULL) __leave;
        }
        else __leave;
    }

    //
    // Indicate success.
    //
    bSuccess=TRUE;

    } // finally
    __finally {

    //
    // Cleanup and indicate failure, if appropriate.
    //

    HeapFree(GetProcessHeap(), 0, ReferencedDomain);

    if(!bSuccess) {
        if(*Sid != NULL) {
            HeapFree(GetProcessHeap(), 0, *Sid);
            *Sid = NULL;
        }
    }

    } // finally

    return bSuccess;
}

NTSTATUS
SetPrivilegeOnAccount(
    LSA_HANDLE PolicyHandle,    // open policy handle
    PSID AccountSid,            // SID to grant privilege to
    LPWSTR PrivilegeName,       // privilege to grant (Unicode)
    BOOL bEnable                // enable or disable
    )
{
    LSA_UNICODE_STRING PrivilegeString;

    //
    // Create a LSA_UNICODE_STRING for the privilege name.
    //
    InitLsaString(&PrivilegeString, PrivilegeName);

    //
    // grant or revoke the privilege, accordingly
    //
    if(bEnable) {
        return LsaAddAccountRights(
                PolicyHandle,       // open policy handle
                AccountSid,         // target SID
                &PrivilegeString,   // privileges
                1                   // privilege count
                );
    }
    else {
        return LsaRemoveAccountRights(
                PolicyHandle,       // open policy handle
                AccountSid,         // target SID
                FALSE,              // do not disable all rights
                &PrivilegeString,   // privileges
                1                   // privilege count
                );
    }
}

void
DisplayNtStatus(
    LPSTR szAPI,
    NTSTATUS Status
    )
{
    //
    // Convert the NTSTATUS to Winerror. Then call DisplayWinError().
    //
    DisplayWinError(szAPI, LsaNtStatusToWinError(Status));
}

void
DisplayWinError(
    LPSTR szAPI,
    DWORD WinError
    )
{
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    //
    // TODO: Get this fprintf out of here!
    //
    fprintf(stderr,"%s error!\n", szAPI);

    if(dwBufferLength=FormatMessageA(
                        FORMAT_MESSAGE_ALLOCATE_BUFFER |
                        FORMAT_MESSAGE_FROM_SYSTEM,
                        NULL,
                        WinError,
                        GetUserDefaultLangID(),
                        (LPSTR) &MessageBuffer,
                        0,
                        NULL
                        ))
    {
        DWORD dwBytesWritten; // unused

        //
        // Output message string on stderr.
        //
        WriteFile(
            GetStdHandle(STD_ERROR_HANDLE),
            MessageBuffer,
            dwBufferLength,
            &dwBytesWritten,
            NULL
            );

        //
        // Free the buffer allocated by the system.
        //
        LocalFree(MessageBuffer);
    }
}


MACHACCT.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    machacct.c

Abstract:

    The following sample illustrates how to create a machine account on the
    specified domain.

    Machine account types are defined by the following flags:

    UF_SERVER_TRUST_ACCOUNT (Backup domain controller)
    UF_WORKSTATION_TRUST_ACCOUNT (Workstation and server)
    UF_INTERDOMAIN_TRUST_ACCOUNT (Interdomain trust account)

    This sample attempts to create a workstation machine account, of type
    UF_WORKSTATION_TRUST_ACCOUNT.  This account type used for workstations
    and non-DC servers which are domain members.

    If the computer account creation fails with GetLastError ==
    ERROR_ACCESS_DENIED, the sample attempts to enable the
    SeMachineAccountPrivilege for the caller. If the privilege is enabled
    successfully, the computer account add operation is re-tried.

    Deleting machine accounts can be accomplished using the NetUserDel()
    Windows NT Lan Manager API call.

    Account update operations against a domain must be performed against
    the primary domain controller for the specified domain.  This sample
    uses the NetGetDCName Windows NT Lan Manager API call to determine
    the computer name of the primary domain controller.

    Commandline parameter argv[1] indicates the name of the account to
    create, which is typically the name of the machine.

    Commandline parameter argv[2] is optional and indicates the target domain.
    If this commandline argument is omitted, the machine account is created
    on the local domain.  It is recommended that you always supply a domain
    name in this sample, as this insures that the update occurs at the primary
    domain controller.

    The following commandline creates a machine account named WINBASE in
    the domain named NTWKSTA

    machacct.exe WINBASE NTWKSTA

Author:

    Scott Field (sfield)    16-Apr-96
        Minor cleanup

    Scott Field (sfield)    19-Sep-95

--*/

#include <windows.h>
#include <stdio.h>
#include <lm.h>

BOOL
AddMachineAccount(
    LPWSTR wTargetComputer, // specifies where to add account
    LPWSTR MachineAccount,  // name of account
    DWORD AccountType       // account type
    );

BOOL
SetCurrentPrivilege(
    LPCTSTR Privilege,      // Privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
    );

void
DisplayError(
    LPSTR szAPI,    // pointer to failed API name
    DWORD dwLastError
    );

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

//
// Unicode entry point and argv
//

int
__cdecl
wmain(
    int argc,
    wchar_t *argv[]
    )
{
    LPWSTR wMachineAccount;
    LPWSTR wPrimaryDC;
    LPTSTR MachineAccountPrivilege = SE_MACHINE_ACCOUNT_NAME;
    DWORD dwTrustAccountType = UF_WORKSTATION_TRUST_ACCOUNT;
    NET_API_STATUS nas;
    BOOL bSuccess;

    if(argc < 2) {
        fprintf(stderr, "Usage: %ls <machineaccountname> [domain]\n",
            argv[0]);
        return RTN_USAGE;
    }

    wMachineAccount = argv[1];

    //
    // if a domain name was specified, fetch the computer name of the
    // primary domain controller
    //
    if(argc == 3) {

        nas = NetGetDCName(NULL, argv[2], (LPBYTE *)&wPrimaryDC);

        if(nas != NERR_Success) {
            DisplayError("NetGetDCName", nas);
            return RTN_ERROR;
        }
    } else {
        //
        // default will operate on local machine.  Non-NULL wPrimaryDC will
        // cause buffer to be freed
        //
        wPrimaryDC = NULL;
    }

    bSuccess = AddMachineAccount(
        wPrimaryDC,         // primary DC computer name
        wMachineAccount,    // computer account name
        dwTrustAccountType  // computer account type
        );

    if(!bSuccess && GetLastError() == ERROR_ACCESS_DENIED ) {

        //
        // try to enable the SeMachineAccountPrivilege
        //
        if(SetCurrentPrivilege( MachineAccountPrivilege, TRUE )) {

            //
            // enabled the privilege.  retry the add operation
            //
            bSuccess = AddMachineAccount(
                wPrimaryDC,
                wMachineAccount,
                dwTrustAccountType
                );

            //
            // disable the privilege
            //
            SetCurrentPrivilege( MachineAccountPrivilege, FALSE );
        }
    }

    //
    // free the buffer allocated for the PDC computer name
    //
    if(wPrimaryDC) NetApiBufferFree(wPrimaryDC);

    if(!bSuccess) {
        DisplayError("AddMachineAccount", GetLastError());
        return RTN_ERROR;
    }

    return RTN_OK;
}

BOOL
AddMachineAccount(
    LPWSTR wTargetComputer,
    LPWSTR MachineAccount,
    DWORD AccountType
    )
{
    WCHAR wAccount[MAX_COMPUTERNAME_LENGTH + 2];
    LPWSTR wPassword;
    USER_INFO_1 ui;
    DWORD cchAccount;
    DWORD cchLength;
    NET_API_STATUS nas;

    //
    // ensure a valid computer account type was passed
    // TODO SetLastError
    //
    if (AccountType != UF_WORKSTATION_TRUST_ACCOUNT &&
        AccountType != UF_SERVER_TRUST_ACCOUNT &&
        AccountType != UF_INTERDOMAIN_TRUST_ACCOUNT
        ) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    //
    // obtain number of chars in computer account name
    //
    cchLength = cchAccount = lstrlenW(MachineAccount);

    //
    // ensure computer name doesn't exceed maximum length
    //
    if(cchLength > MAX_COMPUTERNAME_LENGTH) {
        SetLastError(ERROR_INVALID_ACCOUNT_NAME);
        return FALSE;
    }

    //
    // password is the computer account name converted to lowercase
    // convert the passed MachineAccount in place
    //
    wPassword = MachineAccount;

    //
    // copy MachineAccount to the wAccount buffer allocated while
    // converting computer account name to uppercase.
    // convert password (inplace) to lowercase
    //
    while(cchAccount--) {
        wAccount[cchAccount] = towupper( MachineAccount[cchAccount] );
        wPassword[cchAccount] = towlower( wPassword[cchAccount] );
    }

    //
    // computer account names have a trailing Unicode '$'
    //
    wAccount[cchLength] = L'$';
    wAccount[cchLength + 1] = L'\0'; // terminate the string

    //
    // if the password is greater than the max allowed, truncate
    //
    if(cchLength > LM20_PWLEN) wPassword[LM20_PWLEN] = L'\0';

    //
    // initialize USER_INFO_x structure
    //
    ZeroMemory(&ui, sizeof(ui));

    ui.usri1_name = wAccount;
    ui.usri1_password = wPassword;

    ui.usri1_flags = AccountType | UF_SCRIPT;
    ui.usri1_priv = USER_PRIV_USER;

    nas = NetUserAdd(
                wTargetComputer,    // target computer name
                1,                  // info level
                (LPBYTE) &ui,       // buffer
                NULL
                );

    //
    // indicate whether it was successful
    //
    if(nas == NERR_Success) {
        return TRUE;
    }
    else {
        SetLastError(nas);
        return FALSE;
    }
}

BOOL
SetCurrentPrivilege(
    LPCTSTR Privilege,      // Privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
    )
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    LUID luid;
    TOKEN_PRIVILEGES tpPrevious;
    DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);
    BOOL bSuccess=FALSE;

    if(!LookupPrivilegeValue(NULL, Privilege, &luid)) return FALSE;

    if(!OpenProcessToken(
            GetCurrentProcess(),
            TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
            &hToken
            )) return FALSE;

    //
    // first pass.  get current privilege setting
    //
    tp.PrivilegeCount           = 1;
    tp.Privileges[0].Luid       = luid;
    tp.Privileges[0].Attributes = 0;

    AdjustTokenPrivileges(
            hToken,
            FALSE,
            &tp,
            sizeof(TOKEN_PRIVILEGES),
            &tpPrevious,
            &cbPrevious
            );

    if(GetLastError() == ERROR_SUCCESS) {
        //
        // second pass.  set privilege based on previous setting
        //
        tpPrevious.PrivilegeCount     = 1;
        tpPrevious.Privileges[0].Luid = luid;

        if(bEnablePrivilege) {
            tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
        }
        else {
            tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
                tpPrevious.Privileges[0].Attributes);
        }

        AdjustTokenPrivileges(
                hToken,
                FALSE,
                &tpPrevious,
                cbPrevious,
                NULL,
                NULL
                );

        if(GetLastError() == ERROR_SUCCESS) bSuccess=TRUE;
    }

    CloseHandle(hToken);

    return bSuccess;
}

void
DisplayError(
    LPSTR szAPI,    // pointer to failed API name
    DWORD dwLastError
    )
{
    HMODULE hModule = NULL;
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    fprintf(stderr,"%s error! (rc=%lu)\n", szAPI, dwLastError);

    if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
        hModule = LoadLibraryEx(
            TEXT("netmsg.dll"),
            NULL,
            LOAD_LIBRARY_AS_DATAFILE
            );
    }

    if(dwBufferLength=FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_IGNORE_INSERTS |
        FORMAT_MESSAGE_FROM_SYSTEM |
        ((hModule != NULL) ? FORMAT_MESSAGE_FROM_HMODULE : 0),
        hModule, // module to get message from
        dwLastError,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
        (LPSTR) &MessageBuffer,
        0,
        NULL
        ))
    {
        DWORD dwBytesWritten;

        //
        // Output message string on stderr
        //
        WriteFile(
            GetStdHandle(STD_ERROR_HANDLE),
            MessageBuffer,
            dwBufferLength,
            &dwBytesWritten,
            NULL
            );

        //
        // free the buffer allocated by the system
        //
        LocalFree(MessageBuffer);
    }

    if(hModule != NULL)
        FreeLibrary(hModule);
}


CLIENT.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    client.c

Abstract:

    This sample illustrates a simple client side of the DuplicateTokenEx()
    server sample.

    This client functions correctly on the following platforms:
    Windows NT 3.5
    Windows NT 3.51
    Windows NT 4.0
    Windows 95

Author:

    Scott Field (sfield)    02-Apr-96

--*/

#include <windows.h>
#include <stdio.h>

DWORD
WINAPI
ReadFunc(
    LPVOID lpParam
    );

DWORD
WINAPI
WriteFunc(
    LPVOID lpParam
    );

void
DisplayWinError(
    LPSTR szAPI,    // pointer to Ansi function name
    DWORD dwError   // DWORD WinError
    );

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

int
__cdecl
main(
    int argc,
    char *argv[]
    )
{
    HANDLE hFileRead;
    HANDLE hFileWrite;

    HANDLE hThread;
    DWORD dwThreadId;

    CHAR PipeIn[255];
    CHAR PipeOut[255];

    if(argc == 1) {
        printf("Usage: %s <\\\\server\n", argv[0]);
        printf("  %s \\\\WINBASE (remote server is \\\\winbase)\n", argv[0]);
        printf("  %s \\\\. (local server)\n",   argv[0]);
        return RTN_USAGE;
    }

    //
    // build Pipe names
    //
    wsprintf(PipeIn, "%s\\pipe\\rcmd_out", argv[1]);
    wsprintf(PipeOut, "%s\\pipe\\rcmd_in", argv[1]);

    //
    // wait for the outbound (inbound on server-side) pipe to be available
    //
    WaitNamedPipe(PipeOut, NMPWAIT_WAIT_FOREVER);

    hFileWrite = CreateFile(
        PipeOut,
        GENERIC_WRITE,
        0, // exclusive open
        NULL,
        OPEN_EXISTING,
        SECURITY_SQOS_PRESENT | SECURITY_IMPERSONATION, // server can impersonate
        NULL
        );

    if(hFileWrite == INVALID_HANDLE_VALUE) {
        DisplayWinError("CreateFile", GetLastError());
        return RTN_ERROR;
    }

    //
    // wait for the inbound (outbound on server-side) pipe to be available
    // we time-out in 5 seconds because the server should be ready for us
    // right-away
    //
    WaitNamedPipe(PipeIn, 5 * 1000);

    hFileRead = CreateFile(
        PipeIn,
        GENERIC_READ,
        0, // exclusive open
        NULL,
        OPEN_EXISTING,
        0,
        NULL
        );

    if(hFileRead == INVALID_HANDLE_VALUE) {
        DisplayWinError("CreateFile", GetLastError());
        return RTN_ERROR;
    }

    //
    // start a thread to handle stdin redirection to the pipe
    //
    hThread = CreateThread(
        NULL,
        0,
        WriteFunc,
        (LPVOID)hFileWrite, // parm
        0,
        &dwThreadId
        );

    if(hThread == NULL) {
        DisplayWinError("CreateThread", GetLastError());
        return RTN_ERROR;
    }

    //
    // handle stdout redirection
    //
    ReadFunc((LPVOID)hFileRead);

    //
    // if the read returned, kill the write, cleanup, and then exit
    //
    TerminateThread(hThread, 0xFFFFFFFF);
    CloseHandle(hThread);

    CloseHandle(hFileRead);
    CloseHandle(hFileWrite);

    return RTN_OK;
}

DWORD
WINAPI
ReadFunc(
    LPVOID lpParam
    )
{
    #define BUFFER_SIZE 4096

    HANDLE hFile = (HANDLE)lpParam;
    BYTE lpBuffer[BUFFER_SIZE];

    DWORD dwBytesRead;
    DWORD dwBytesWritten;

    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    DWORD dwLastError;

    while (1) {
        if(!ReadFile(
            hFile,
            lpBuffer,
            BUFFER_SIZE,
            &dwBytesRead,
            NULL
            )) {
            dwLastError = GetLastError();

            if(dwLastError == ERROR_NO_DATA) continue;
            if(dwLastError != ERROR_MORE_DATA) break;
        }

        if(!WriteFile(
            hStdOut,
            lpBuffer,
            dwBytesRead,
            &dwBytesWritten,
            NULL
            )) break;
    }

    return GetLastError();
}

DWORD
WINAPI
WriteFunc(
    LPVOID lpParam
    )
{
    HANDLE hPipe = (HANDLE)lpParam;

    BYTE lpBuffer[ 1 ]; // change this later

    DWORD dwBytesRead;
    DWORD dwBytesWritten;

    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);

    while (1) {
        //
        // TODO rework this loop to be line-based.
        // this would entail doing the console buffering outselves.
        //
        if(!ReadFile(
            hStdIn,
            lpBuffer,
            1,
            &dwBytesRead,
            NULL
            )) break;

        if(!WriteFile(
            hPipe,
            lpBuffer,
            dwBytesRead,
            &dwBytesWritten,
            NULL
            )) break;
    }

    return GetLastError();
}

void
DisplayWinError(
    LPSTR szAPI,    // pointer to Ansi function name
    DWORD dwError   // DWORD WinError
    )
{
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    //
    // TODO get this fprintf out of here!
    //
    fprintf(stderr,"%s error!\n", szAPI);

    if(dwBufferLength=FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM,
            NULL,
            dwError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPSTR) &MessageBuffer,
            0,
            NULL
            ))
    {
        DWORD dwBytesWritten; // unused

        //
        // Output message string on stderr
        //
        WriteFile(
                GetStdHandle(STD_ERROR_HANDLE),
                MessageBuffer,
                dwBufferLength,
                &dwBytesWritten,
                NULL
                );

        //
        // free the buffer allocated by the system
        //
        LocalFree(MessageBuffer);
    }
}


SERVER.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    server.c

Abstract:

    This sample illustrates the new DuplicateTokenEx() API for Windows NT 4.0.
    This portion of the sample illustrates how to implement a remote cmd
    server which launches cmd.exe in the security context of the client that
    connected to the server.

    When a client connects to the named-pipe, the server process impersonates
    the client and then saves a copy of the impersonation token.  This token
    is then duplicated via DuplicateTokenEx() to a primary level access token.
    The primary level token is then supplied to CreateProcessAsUser(), which
    launches a process in the security context of the client.  All threads in
    the new process inherit the security context of the client.  All processes
    created by the new process inherit the security context of the client.  The
    input and output of the new process will be redirected to the client over
    the named-pipes, which provides a means of interactively executing commands
    remotely in a secure manner.

    In order to allow processes to run in a different security context than
    the server process, it is necessary to adjust the security on the
    windowstation and desktop objects associated with the server process.
    This is required to allow for proper console initialization.

    For simplicity, this sample applies a Null Dacl to the windowstation and
    desktop objects, which is generally not appropriate in a production
    environment.  The best approach to selectively secure the windowstation and
    desktop objects is to extract the Logon Sid from the access token returned
    by DuplicateTokenEx().  The Logon Sid can be extracted with
    GetTokenInformation() and then added to an access allowed ace which is
    supplied in a Dacl which contains the existing access allowed aces in
    addition to the new access allowed ace.

    Some additional considerations with this sample follow:

    * The account which the server process runs in needs the following two
      privileges granted, to allow for the call to CreateProcessAsUser() to
      succeed:

      SeAssignPrimaryTokenPrivilege (Replace a process level token)
      SeIncreateQuotaPrivilege (Increase quotas)

    * The security on the initial server side named-pipe allows for Everyone
      to connect.  This may present security problems in the event that the
      server's file system has not be secured appropriately.  In a production
      environment, it may be appropriate to apply a more restrictive Dacl on
      the initial named-pipe.

    * This sample only allows one client to be connected at a time.

    * Any child processes started by the launched process will not be terminated
      when the initial child process exits.

Author:

    Scott Field (sfield)    02-Apr-96

--*/

#include <windows.h>
#include <stdio.h>

BOOL
BuildNamedPipeAcl(
    PACL pAcl,
    PDWORD cbAclSize
    );

BOOL
SetupNamedPipes(
    PSECURITY_ATTRIBUTES saInbound,
    PSECURITY_ATTRIBUTES saOutbound,
    PHANDLE hFileIn,
    PHANDLE hFileOut
    );

BOOL
SetWinstaDesktopSecurity(
    void
    );

void
DisplayLastError(
    LPSTR szAPI // pointer to Ansi function name
    );

//
// this defines the commandline that the server will execute in the security
// context of the client.  The /Q switch tells cmd.exe not to echo the
// entered commands back to the client.
//
#define COMMANDLINE     TEXT("cmd.exe /Q")

#define INBOUND_PIPE    TEXT("\\\\.\\pipe\\rcmd_in")
#define OUTBOUND_PIPE   TEXT("\\\\.\\pipe\\rcmd_out")

#define RTN_OK 0
#define RTN_ERROR 13

int
__cdecl
main(
    void
    )
{
    HANDLE hPipeInbound;
    HANDLE hPipeOutbound;

    SECURITY_ATTRIBUTES saInbound;
    SECURITY_DESCRIPTOR sd;

    BYTE AclBuf[ 64 ];
    DWORD cbAclSize = 64;
    PACL pAcl = (PACL)AclBuf;

    SECURITY_ATTRIBUTES saOutbound;

    HANDLE hImpersonationToken;
    HANDLE hPrimaryToken;

    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    //
    // suppress errors regarding startup directory, etc
    //
    SetErrorMode(SEM_FAILCRITICALERRORS);

    //
    // build security attributes for named-pipes
    //
    // the inbound pipe will grant Everyone the ability to connect,
    // and the server process full control over the pipe.  This way,
    // only the server process has the ability to update security
    // on the resource; clients can only connect.
    //
    // the outbound pipe will be built after the server impersonates
    // the client, and will inherit the default security of the client
    // as a result.  This allows only the same user that connected to
    // the inbound pipe to connect to the outbound pipe
    //

    if(!BuildNamedPipeAcl(pAcl, &cbAclSize)) {
        DisplayLastError("BuildNamedPipeAcl");
        return RTN_ERROR;
    }

    if(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
        DisplayLastError("InitializeSecurityDescriptor");
        return RTN_ERROR;
    }

    if(!SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) {
        DisplayLastError("SetSecurityDescriptorDacl");
        return RTN_ERROR;
    }

    saInbound.nLength = sizeof(SECURITY_ATTRIBUTES);
    saInbound.lpSecurityDescriptor = &sd;
    saInbound.bInheritHandle = TRUE;

    saOutbound.nLength = sizeof(SECURITY_ATTRIBUTES);
    saOutbound.lpSecurityDescriptor = NULL; // default Dacl of caller
    saOutbound.bInheritHandle = TRUE;

    //
    // modify security on windowstation and desktop to allow for correct
    // process and console initialization by arbitrary clients
    //
    if(!SetWinstaDesktopSecurity()) {
        DisplayLastError("SetWinstaDesktopSecurity");
        return RTN_ERROR;
    }

    while(1) {

    hPipeInbound = INVALID_HANDLE_VALUE;
    hPipeOutbound = INVALID_HANDLE_VALUE;
    hImpersonationToken = INVALID_HANDLE_VALUE;
    hPrimaryToken = INVALID_HANDLE_VALUE;
    pi.hProcess = INVALID_HANDLE_VALUE;
    pi.hThread = INVALID_HANDLE_VALUE;

    if(!SetupNamedPipes(
        &saInbound,
        &saOutbound,
        &hPipeInbound,
        &hPipeOutbound
        )) {

        //
        // just bail out on failure for now
        //
        DisplayLastError("SetupNamedPipes");
        break;
    }

    //
    // obtain the impersonation token from the current thread
    //
    if(!OpenThreadToken(
        GetCurrentThread(),
        TOKEN_DUPLICATE,
        TRUE,
        &hImpersonationToken
        )) {
        DisplayLastError("OpenThreadToken");
        goto cleanup;
    }

    //
    // duplicate the impersonation token to primary
    // since we are impersonating the client, the token will get the
    // default Dacl of the client
    //
    if(!DuplicateTokenEx(
        hImpersonationToken,
        TOKEN_IMPERSONATE | TOKEN_READ |
        TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE,
        NULL,
        SecurityImpersonation,
        TokenPrimary,
        &hPrimaryToken
        )) {
        DisplayLastError("DuplicateTokenEx");
        goto cleanup;
    }

    RevertToSelf();
    CloseHandle(hImpersonationToken);
    hImpersonationToken = INVALID_HANDLE_VALUE;

    //
    // setup STARTUPINFO structure
    //
    si.cb = sizeof(STARTUPINFO);
    si.lpReserved = NULL;
    si.lpDesktop = NULL;
    si.lpTitle = NULL;
    si.cbReserved2 = 0;
    si.lpReserved2 = NULL;
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdOutput = hPipeOutbound;
    si.hStdError = hPipeOutbound;
    si.hStdInput = hPipeInbound;

    //
    // create a process running as the user
    //

    if(!CreateProcessAsUser(
        hPrimaryToken,
        NULL,
        COMMANDLINE, // commandline to execute
        NULL,   // process sa
        NULL,   // thread sa
        TRUE,   // inherit handles?
        0,      // process creation flags (inherit existing console)
        NULL,   // environment
        NULL,   // current directory
        &si,    // startupinfo
        &pi     // processinfo
        )) {
        DisplayLastError("CreateProcessAsUser");
        goto cleanup;
    }

cleanup:

    if(hImpersonationToken != INVALID_HANDLE_VALUE) {
        RevertToSelf();
        CloseHandle(hImpersonationToken);
    }

    if(hPrimaryToken != INVALID_HANDLE_VALUE) {
        CloseHandle(hPrimaryToken);
    }

    if(pi.hThread != INVALID_HANDLE_VALUE) {
        CloseHandle(pi.hThread);
    }

    if(pi.hProcess != INVALID_HANDLE_VALUE) {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);

        //
        // TODO kill any child by enumerating process tokens in the system
        // by looking at the tokenID
        //
    }

    if(hPipeInbound != INVALID_HANDLE_VALUE) {
        DisconnectNamedPipe(hPipeInbound);
        CloseHandle(hPipeInbound);
    }

    if(hPipeOutbound != INVALID_HANDLE_VALUE) {
        DisconnectNamedPipe(hPipeOutbound);
        CloseHandle(hPipeOutbound);
    }

    } // while

    return RTN_OK;
}

/***
 If the function succeeds, the return value is TRUE.  The parameters
 hFileIn and hFileOut will point to the Inbound and Outbound named-pipe
 handles.  The current thread will be impersonating the client.

 If the function fails, the return value is FALSE.  The current thread will
 not be impersonating the client.
***/
BOOL
SetupNamedPipes(
    PSECURITY_ATTRIBUTES saInbound, // security attributes for Inbound pipe
    PSECURITY_ATTRIBUTES saOutbound,// security attributes for Outbound pipe
    PHANDLE hFileIn,                // resultant Inbound pipe handle on success
    PHANDLE hFileOut                // resultant Outbound pipe handle on success
    )
{
    HANDLE hPipeInbound = INVALID_HANDLE_VALUE;
    HANDLE hPipeOutbound = INVALID_HANDLE_VALUE;

    BOOL bSuccess = FALSE; // assume this function fails

    //
    // create Inbound named-pipe
    //
    hPipeInbound = CreateNamedPipe(
        INBOUND_PIPE,
        PIPE_ACCESS_INBOUND,
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
        1, // number of instances
        4096,
        4096,
        NMPWAIT_USE_DEFAULT_WAIT,
        saInbound
        );

    if(hPipeInbound == INVALID_HANDLE_VALUE) {
        DisplayLastError("CreateNamedPipe");
        return FALSE;
    }

    printf("Waiting for connection... ");

    //
    // wait for somebody to connect to the Inbound named pipe
    //
    ConnectNamedPipe(hPipeInbound, NULL);

    //
    // impersonate the client
    //
    if(!ImpersonateNamedPipeClient(hPipeInbound)) {
        DisplayLastError("ImpersonateNamedPipeClient");
        goto cleanup;
    }

    //
    // create Outbound named-pipe
    // the security on this named-pipe will be inherited from the default Dacl
    // of the client that just connected, because this pipe instance is
    // created in that users security context.  This prevents other users
    // from connecting to the Outbound pipe.
    //
    hPipeOutbound = CreateNamedPipe(
        OUTBOUND_PIPE,
        PIPE_ACCESS_OUTBOUND,
        PIPE_TYPE_MESSAGE | PIPE_WAIT,
        1, // number of instances
        4096,
        4096,
        NMPWAIT_USE_DEFAULT_WAIT,
        saOutbound
        );

    if(hPipeOutbound == INVALID_HANDLE_VALUE) {
        DisplayLastError("CreateNamedPipe");
        goto cleanup;
    }

    //
    // wait for the client to connect to the Outbound named pipe
    //
    ConnectNamedPipe(hPipeOutbound, NULL);

    printf("Client connected!\n");

    *hFileIn = hPipeInbound;
    *hFileOut = hPipeOutbound;

    bSuccess = TRUE;

cleanup:

    if(!bSuccess) {
        RevertToSelf();

        if(hPipeInbound != INVALID_HANDLE_VALUE)
            CloseHandle(hPipeInbound);
        if(hPipeOutbound != INVALID_HANDLE_VALUE)
            CloseHandle(hPipeOutbound);
    }

    return bSuccess;
}

/**
This function builds a Dacl which grants the creator of the objects
FILE_ALL_ACCESS and Everyone FILE_GENERIC_READ and FILE_GENERIC_WRITE
access to the object.

This Dacl allows for higher security than a NULL Dacl, which is common for
named-pipes, as this only grants the creator/owner write access to the
security descriptor, and grants Everyone the ability to "use" the named-pipe.
This scenario prevents a malevolent user from disrupting service by preventing
arbitrary access manipulation.
**/
BOOL
BuildNamedPipeAcl(
    PACL pAcl,
    PDWORD cbAclSize
    )
{
    DWORD dwAclSize;

    SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY;

    BYTE BufEveryoneSid[32];
    BYTE BufOwnerSid[32];

    PSID pEveryoneSid = (PSID)BufEveryoneSid;
    PSID pOwnerSid = (PSID)BufOwnerSid;

    //
    // compute size of acl
    //
    dwAclSize = sizeof(ACL) +
        2 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
        GetSidLengthRequired( 1 ) + // well-known Everyone Sid
        GetSidLengthRequired( 1 ) ; // well-known Creator Owner Sid

    if(*cbAclSize < dwAclSize) {
        *cbAclSize = dwAclSize;
        return FALSE;
    }

    *cbAclSize = dwAclSize;

    //
    // intialize well known sids
    //

    if(!InitializeSid(pEveryoneSid, &siaWorld, 1)) return FALSE;
    *GetSidSubAuthority(pEveryoneSid, 0) = SECURITY_WORLD_RID;

    if(!InitializeSid(pOwnerSid, &siaCreator, 1)) return FALSE;
    *GetSidSubAuthority(pOwnerSid, 0) = SECURITY_CREATOR_OWNER_RID;

    if(!InitializeAcl(pAcl, dwAclSize, ACL_REVISION))
        return FALSE;

    //
    //
    if(!AddAccessAllowedAce(
        pAcl,
        ACL_REVISION,
        FILE_GENERIC_READ | FILE_GENERIC_WRITE,
        pEveryoneSid
        ))
        return FALSE;

    //
    //
    return AddAccessAllowedAce(
        pAcl,
        ACL_REVISION,
        FILE_ALL_ACCESS,
        pOwnerSid
        );
}

/**
This function adjusts the security on the current windowstation and desktop,
to allow arbitrary client to have access to the windowstation and desktop.
This is necessary to allow for correct process and console initialization.

Note that in a secure environment, it would be appropriate to create
a specific desktop and launch the process spawned by the client on that
desktop, rather than opening up the current desktop to the client.

This function simply applies a NULL Dacl to the current windowstation and
desktop in order to reduce the size of this sample.  Applying NULL Dacls
to objects is generally not a wise idea.

TODO revist this function later to better address security on the windowstation
and desktop.
**/
BOOL
SetWinstaDesktopSecurity(
    void
    )
{
    HWINSTA hWinsta;
    HDESK hDesk;

    SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
    SECURITY_DESCRIPTOR sd;

    hWinsta = GetProcessWindowStation();
    if(hWinsta == NULL) return FALSE;

    hDesk = GetThreadDesktop(GetCurrentThreadId());
    if(hDesk == NULL) return FALSE;

    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(&sd, TRUE, (PACL)NULL, FALSE);

    if(!SetUserObjectSecurity(hWinsta, &si, &sd)) return FALSE;

    return SetUserObjectSecurity(hDesk, &si, &sd);
}

void
DisplayLastError(
    LPSTR szAPI // pointer to Ansi function name
    )
{
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    //
    // TODO get this fprintf out of here!
    //
    fprintf(stderr,"%s error!\n", szAPI);

    if(dwBufferLength=FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM,
            NULL,
            GetLastError(),
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPSTR) &MessageBuffer,
            0,
            NULL
            ))
    {
        DWORD dwBytesWritten; // unused

        //
        // Output message string on stderr
        //
        WriteFile(
                GetStdHandle(STD_ERROR_HANDLE),
                MessageBuffer,
                dwBufferLength,
                &dwBytesWritten,
                NULL
                );

        //
        // free the buffer allocated by the system
        //
        LocalFree(MessageBuffer);
    }
}


README.TXT

AuthPkg

Some of the APIs used in this sample are not Win32 APIs.  They are
specific APIs for Security Providers.

This sample demonstrates the implementation of a Windows NT
Authentication Package. Other examples of authentication packages
include NTLM and Kerberos which are provided by the Windows NT
operating system.

This sample does not use any kind of "real" credentials, it just
provides data that will be used by the package to allow  client
and server applications to authenticate each other. It is expected
that a real provider would create\provide credentials based on
some security framework not implemented by Windows NT.

This authentication package can be used with the SOCKAUTH sample
with some minor modifications to the SOCKAUTH sample so that it
requests this authentication package specifically.

To invoke the sample package, copy the DLL to the %systemroot%\system32
directory and make the following changes to the SOCKAUTH security.c
file:

wchar_t NT_DLL_NAME[12] = L"sampssp.dll";
wchar_t PACKAGE_NAME[12] = L"SAMPSSP";
wchar_t TOKEN_SOURCE_NAME[12] = L"AuthSamp";


INIT.C

/*++

Copyright 1993 - 1998 Microsoft Corporation

Module Name:

    init.c

Abstract:

    NT LM Security Support Provider client side initialization.

Author:

    Cliff Van Dyke (CliffV) 29-Jun-1993

Environment:  User Mode

Revision History:

--*/

#include <sampssp.h>

CRITICAL_SECTION DllCritSect;    // Serializes access to all globals in module

BOOLEAN
DllInit(
    IN PVOID DllHandle,
    IN ULONG Reason,
    IN PCONTEXT Context OPTIONAL
    )

/*++

Routine Description:

    This is the Dll initialization routine for ntlmssp.dll

Arguments:

    Standard.

Return Status:

    TRUE: if initialization succeeded

--*/

{

    //
    // On process attach,
    //      initialize the critical section,
    //      defer any additional initialization.
    //

    switch (Reason) {
    case DLL_PROCESS_ATTACH:
_asm int 3
        InitializeCriticalSection( &DllCritSect );
        break;

    //
    // Handle process detach.
    //

    case DLL_PROCESS_DETACH:

        //
        // Shutdown the common routines.
        //

        EnterCriticalSection( &DllCritSect );
        LeaveCriticalSection( &DllCritSect );

        //
        // Finally, Delete the critical section
        //

        DeleteCriticalSection( &DllCritSect );

        break;

    }

    return TRUE;
    UNREFERENCED_PARAMETER( Context );
    UNREFERENCED_PARAMETER( DllHandle );

}


SAMPSSP.H

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif // WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifndef SECURITY_WIN32
#define SECURITY_WIN32
#endif // SECURITY_WIN32
#include "sspi.h"
#include "issperr.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

extern CRITICAL_SECTION DllCritSect;

typedef struct _SEC_CONTEXT {
    struct _SEC_CONTEXT * Next;
    ULONG ContextId;
    ULONG CredentialId;
    ULONG Nonce;
    ULONG ContextFlags;
    enum {
        Idle,
        FirstInit,
        FirstAccept,
        SecondInit,
        SecondAccept
    } State;
} SEC_CONTEXT, *PSEC_CONTEXT;

typedef struct _CREDENTIAL {
    struct _CREDENTIAL *Next;
    ULONG CredentialId;
    ULONG Use;
    ULONG AuthData;
} CREDENTIAL, *PCREDENTIAL;

typedef enum {
    Negotiate,
    Challenge,
    ChallengeResponse,
    ReAuthenticate
} MESSAGE_TYPE;

#define MESSAGE_SIZE 10

typedef struct _MESSAGE {
    MESSAGE_TYPE MessageType;
    UCHAR Buffer[MESSAGE_SIZE];
} MESSAGE, *PMESSAGE;

typedef struct _SIGNATURE {
    ULONG MessageId;
    ULONG Tag;
} SIGNATURE, *PSIGNATURE;

#define PACKAGE_NAME            L"SAMPSSP"
#define PACKAGE_COMMENT         L"Sample package for testing Security Providers"
#define PACKAGE_CAPABILITIES    (SECPKG_FLAG_TOKEN_ONLY | \
                                 SECPKG_FLAG_MULTI_REQUIRED | \
                                 SECPKG_FLAG_CONNECTION | \
                                 SECPKG_FLAG_INTEGRITY | \
                                 SECPKG_FLAG_PRIVACY)
#define PACKAGE_VERSION         1
//
// This is the number we enter in
// Registry\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Rpc\SecurityService
// for example: 123 = REG_SZ "sampssp.dll"
//
#define PACKAGE_RPCID           123
#define PACKAGE_MAXTOKEN        sizeof(MESSAGE)
#define PACKAGE_SIGNATURE_SIZE  sizeof(SIGNATURE)
#define PACKAGE_SIGNATURE_TAG   0xbaadf00d


SSPI.H

//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992-1996.
//
//  File:       sspi.h
//
//  Contents:   Security Support Provider Interface
//              Prototypes and structure definitions
//
//  Functions:  Security Support Provider API
//
//
//----------------------------------------------------------------------------

#ifndef __SSPI_H__
#define __SSPI_H__

//
// Determine environment:
//

#ifdef SECURITY_WIN32
#define ISSP_LEVEL  32
#define ISSP_MODE   1
#endif // SECURITY_WIN32

#ifdef SECURITY_WIN16
#define ISSP_LEVEL  16
#define ISSP_MODE   1
#endif // SECURITY_WIN16

#ifdef SECURITY_KERNEL
#define ISSP_LEVEL  32

//
// SECURITY_KERNEL trumps SECURITY_WIN32.  Undefine ISSP_MODE so that
// we don't get redefine errors.
//
#ifdef ISSP_MODE
#undef ISSP_MODE
#endif
#define ISSP_MODE   0
#endif // SECURITY_KERNEL

#ifdef SECURITY_OS212
#define ISSP_LEVEL  16
#define ISSP_MODE   1
#endif // SECURITY_OS212

#ifdef SECURITY_DOS
#define ISSP_LEVEL  16
#define ISSP_MODE   1
#endif // SECURITY_DOS

#ifdef SECURITY_MAC
#define ISSP_LEVEL  32
#define ISSP_MODE   1
#endif // SECURITY_MAC

#ifndef ISSP_LEVEL
#error  You must define one of SECURITY_WIN32, SECURITY_WIN16, SECURITY_KERNEL
#error  SECURITY_DOS, SECURITY_MAC or SECURITY_OS212
#endif // !ISSP_LEVEL

//
// Now, define platform specific mappings:
//

#if ISSP_LEVEL == 16

typedef short SECURITY_STATUS;
typedef short HRESULT;
typedef unsigned short SEC_WCHAR;
typedef char SEC_CHAR;
#define SEC_TEXT(_x_) _x_

#ifdef SECURITY_WIN16

#define SEC_FAR __far
#define SEC_ENTRY __pascal __far __export

#else // SECURITY_WIN16

#define SEC_FAR __far
#define SEC_ENTRY __pascal __far __loadds
#pragma warning(disable:4147)

#endif // SECURITY_WIN16

#elif defined(SECURITY_MAC)  // ISSP_LEVEL == 16

#define SEC_ENTRY
#define SEC_TEXT(_X_) _X_
#define SEC_FAR

typedef unsigned short SEC_WCHAR;
typedef char SEC_CHAR;
typedef long    HRESULT;
typedef HRESULT SECURITY_STATUS;

// No Unicode on the Mac

typedef SEC_CHAR SEC_FAR * SECURITY_PSTR;
typedef SEC_CHAR SEC_FAR * SECURITY_PCSTR;

#else // ISSP_LEVEL == 16

//
// For NT-2 and up, wtypes will define HRESULT to be long.
//

typedef WCHAR SEC_WCHAR;
typedef CHAR SEC_CHAR;

#if !defined(__wtypes_h__) || defined(SECURITY_KERNEL)
typedef long    HRESULT;
#endif // wtypes.h || security_kernel

typedef HRESULT SECURITY_STATUS;

#define SEC_TEXT TEXT
#define SEC_FAR
#define SEC_ENTRY __stdcall

//
// Decide what a string - 32 bits only since for 16 bits it is clear.
//

#ifdef UNICODE
typedef SEC_WCHAR SEC_FAR * SECURITY_PSTR;
typedef CONST SEC_WCHAR SEC_FAR * SECURITY_PCSTR;
#else // UNICODE
typedef SEC_CHAR SEC_FAR * SECURITY_PSTR;
typedef CONST SEC_CHAR SEC_FAR * SECURITY_PCSTR;
#endif // UNICODE

#endif // ISSP_LEVEL == 16

//
// Equivalent string for rpcrt:
//

#define __SEC_FAR SEC_FAR

//
// Okay, security specific types:
//

typedef struct _SecHandle
{
    unsigned long dwLower;
    unsigned long dwUpper;
} SecHandle, SEC_FAR * PSecHandle;

typedef SecHandle CredHandle;
typedef PSecHandle PCredHandle;

typedef SecHandle CtxtHandle;
typedef PSecHandle PCtxtHandle;

#if ISSP_LEVEL == 32

#  ifdef WIN32_CHICAGO

typedef unsigned __int64 QWORD;
typedef QWORD SECURITY_INTEGER, *PSECURITY_INTEGER;

#  elif defined(_NTDEF_) || defined(_WINNT_)

typedef LARGE_INTEGER _SECURITY_INTEGER, SECURITY_INTEGER, *PSECURITY_INTEGER;

#  else // _NTDEF_ || _WINNT_

// BUGBUG:  Alignment for axp

typedef struct _SECURITY_INTEGER
{
    unsigned long LowPart;
    long HighPart;
} SECURITY_INTEGER, *PSECURITY_INTEGER;

#  endif // _NTDEF_ || _WINNT_

#  ifndef SECURITY_MAC
typedef SECURITY_INTEGER TimeStamp;
typedef SECURITY_INTEGER SEC_FAR * PTimeStamp;
#  else // SECURITY_MAC
typedef unsigned long TimeStamp;
typedef unsigned long * PTimeStamp;
#  endif // SECUIRT_MAC

#else // ISSP_LEVEL == 32

typedef unsigned long TimeStamp;
typedef unsigned long SEC_FAR * PTimeStamp;

#endif // ISSP_LEVEL == 32

//
// If we are in 32 bit mode, define the SECURITY_STRING structure,
// as a clone of the base UNICODE_STRING structure.  This is used
// internally in security components, an as the string interface
// for kernel components (e.g. FSPs)
//

#if ISSP_LEVEL == 32
#  ifndef _NTDEF_
typedef struct _SECURITY_STRING {
    unsigned short      Length;
    unsigned short      MaximumLength;
#    ifdef MIDL_PASS
    [size_is(MaximumLength / 2), length_is(Length / 2)]
#    endif // MIDL_PASS
    unsigned short *    Buffer;
} SECURITY_STRING, * PSECURITY_STRING;
#  else // _NTDEF_
typedef UNICODE_STRING SECURITY_STRING, *PSECURITY_STRING;
#  endif // _NTDEF_
#endif // ISSP_LEVEL == 32

//
// SecPkgInfo structure
//
//  Provides general information about a security provider
//

typedef struct _SecPkgInfoW
{
    unsigned long fCapabilities;        // Capability bitmask
    unsigned short wVersion;            // Version of driver
    unsigned short wRPCID;              // ID for RPC Runtime
    unsigned long cbMaxToken;           // Size of authentication token (max)
#ifdef MIDL_PASS
    [string]
#endif
    SEC_WCHAR SEC_FAR * Name;           // Text name

#ifdef MIDL_PASS
    [string]
#endif
    SEC_WCHAR SEC_FAR * Comment;        // Comment
} SecPkgInfoW, SEC_FAR * PSecPkgInfoW;

typedef struct _SecPkgInfoA
{
    unsigned long fCapabilities;        // Capability bitmask
    unsigned short wVersion;            // Version of driver
    unsigned short wRPCID;              // ID for RPC Runtime
    unsigned long cbMaxToken;           // Size of authentication token (max)
#ifdef MIDL_PASS
    [string]
#endif
    SEC_CHAR SEC_FAR * Name;            // Text name

#ifdef MIDL_PASS
    [string]
#endif
    SEC_CHAR SEC_FAR * Comment;         // Comment
} SecPkgInfoA, SEC_FAR * PSecPkgInfoA;

#ifdef UNICODE
#  define SecPkgInfo SecPkgInfoW
#  define PSecPkgInfo PSecPkgInfoW
#else
#  define SecPkgInfo SecPkgInfoA
#  define PSecPkgInfo PSecPkgInfoA
#endif // !UNICODE

//
//  Security Package Capabilities
//
#define SECPKG_FLAG_INTEGRITY       0x00000001  // Supports integrity on messages
#define SECPKG_FLAG_PRIVACY         0x00000002  // Supports privacy (confidentiality)
#define SECPKG_FLAG_TOKEN_ONLY      0x00000004  // Only security token needed
#define SECPKG_FLAG_DATAGRAM        0x00000008  // Datagram RPC support
#define SECPKG_FLAG_CONNECTION      0x00000010  // Connection oriented RPC support
#define SECPKG_FLAG_MULTI_REQUIRED  0x00000020  // Full 3-leg required for re-auth.
#define SECPKG_FLAG_CLIENT_ONLY     0x00000040  // Server side functionality not available
#define SECPKG_FLAG_EXTENDED_ERROR  0x00000080  // Supports extended error msgs
#define SECPKG_FLAG_IMPERSONATION   0x00000100  // Supports impersonation
#define SECPKG_FLAG_ACCEPT_WIN32_NAME   0x00000200  // Accepts Win32 names
#define SECPKG_FLAG_STREAM          0x00000400  // Supports stream semantics

#define SECPKG_ID_NONE      0xFFFF

//
// SecBuffer
//
//  Generic memory descriptors for buffers passed in to the security
//  API
//

typedef struct _SecBuffer {
    unsigned long cbBuffer;             // Size of the buffer, in bytes
    unsigned long BufferType;           // Type of the buffer (below)
    void SEC_FAR * pvBuffer;            // Pointer to the buffer
} SecBuffer, SEC_FAR * PSecBuffer;

typedef struct _SecBufferDesc {
    unsigned long ulVersion;            // Version number
    unsigned long cBuffers;             // Number of buffers
#ifdef MIDL_PASS
    [size_is(cBuffers)]
#endif
    PSecBuffer pBuffers;                // Pointer to array of buffers
} SecBufferDesc, SEC_FAR * PSecBufferDesc;

#define SECBUFFER_VERSION           0

#define SECBUFFER_EMPTY             0   // Undefined, replaced by provider
#define SECBUFFER_DATA              1   // Packet data
#define SECBUFFER_TOKEN             2   // Security token
#define SECBUFFER_PKG_PARAMS        3   // Package specific parameters
#define SECBUFFER_MISSING           4   // Missing Data indicator
#define SECBUFFER_EXTRA             5   // Extra data
#define SECBUFFER_STREAM_TRAILER    6   // Security Trailer
#define SECBUFFER_STREAM_HEADER     7   // Security Header

#define SECBUFFER_ATTRMASK          0xF0000000
#define SECBUFFER_READONLY          0x80000000  // Buffer is read-only

//
//  Data Representation Constant:
//
#define SECURITY_NATIVE_DREP        0x00000010

//
//  Credential Use Flags
//
#define SECPKG_CRED_INBOUND         0x00000001
#define SECPKG_CRED_OUTBOUND        0x00000002
#define SECPKG_CRED_BOTH            0x00000003

//
//  InitializeSecurityContext Requirement and return flags:
//

#define ISC_REQ_DELEGATE                0x00000001
#define ISC_REQ_MUTUAL_AUTH             0x00000002
#define ISC_REQ_REPLAY_DETECT           0x00000004
#define ISC_REQ_SEQUENCE_DETECT         0x00000008
#define ISC_REQ_CONFIDENTIALITY         0x00000010
#define ISC_REQ_USE_SESSION_KEY         0x00000020
#define ISC_REQ_PROMPT_FOR_CREDS        0x00000040
#define ISC_REQ_USE_SUPPLIED_CREDS      0x00000080
#define ISC_REQ_ALLOCATE_MEMORY         0x00000100
#define ISC_REQ_USE_DCE_STYLE           0x00000200
#define ISC_REQ_DATAGRAM                0x00000400
#define ISC_REQ_CONNECTION              0x00000800
#define ISC_REQ_CALL_LEVEL              0x00001000
#define ISC_REQ_EXTENDED_ERROR          0x00004000
#define ISC_REQ_STREAM                  0x00008000
#define ISC_REQ_INTEGRITY               0x00010000
#define ISC_REQ_IDENTIFY                0x00020000

#define ISC_RET_DELEGATE                0x00000001
#define ISC_RET_MUTUAL_AUTH             0x00000002
#define ISC_RET_REPLAY_DETECT           0x00000004
#define ISC_RET_SEQUENCE_DETECT         0x00000008
#define ISC_RET_CONFIDENTIALITY         0x00000010
#define ISC_RET_USE_SESSION_KEY         0x00000020
#define ISC_RET_USED_COLLECTED_CREDS    0x00000040
#define ISC_RET_USED_SUPPLIED_CREDS     0x00000080
#define ISC_RET_ALLOCATED_MEMORY        0x00000100
#define ISC_RET_USED_DCE_STYLE          0x00000200
#define ISC_RET_DATAGRAM                0x00000400
#define ISC_RET_CONNECTION              0x00000800
#define ISC_RET_INTERMEDIATE_RETURN     0x00001000
#define ISC_RET_CALL_LEVEL              0x00002000
#define ISC_RET_EXTENDED_ERROR          0x00004000
#define ISC_RET_STREAM                  0x00008000
#define ISC_RET_INTEGRITY               0x00010000
#define ISC_RET_IDENTIFY                0x00020000

#define ASC_REQ_DELEGATE                0x00000001
#define ASC_REQ_MUTUAL_AUTH             0x00000002
#define ASC_REQ_REPLAY_DETECT           0x00000004
#define ASC_REQ_SEQUENCE_DETECT         0x00000008
#define ASC_REQ_CONFIDENTIALITY         0x00000010
#define ASC_REQ_USE_SESSION_KEY         0x00000020
#define ASC_REQ_ALLOCATE_MEMORY         0x00000100
#define ASC_REQ_USE_DCE_STYLE           0x00000200
#define ASC_REQ_DATAGRAM                0x00000400
#define ASC_REQ_CONNECTION              0x00000800
#define ASC_REQ_CALL_LEVEL              0x00001000
#define ASC_REQ_EXTENDED_ERROR          0x00008000
#define ASC_REQ_STREAM                  0x00010000
#define ASC_REQ_INTEGRITY               0x00020000
#define ASC_REQ_LICENSING               0x00040000

#define ASC_RET_DELEGATE                0x00000001
#define ASC_RET_MUTUAL_AUTH             0x00000002
#define ASC_RET_REPLAY_DETECT           0x00000004
#define ASC_RET_SEQUENCE_DETECT         0x00000008
#define ASC_RET_CONFIDENTIALITY         0x00000010
#define ASC_RET_USE_SESSION_KEY         0x00000020
#define ASC_RET_ALLOCATED_MEMORY        0x00000100
#define ASC_RET_USED_DCE_STYLE          0x00000200
#define ASC_RET_DATAGRAM                0x00000400
#define ASC_RET_CONNECTION              0x00000800
#define ASC_RET_CALL_LEVEL              0x00002000 // skipped 1000 to be like ISC_
#define ASC_RET_THIRD_LEG_FAILED        0x00004000
#define ASC_RET_EXTENDED_ERROR          0x00008000
#define ASC_RET_STREAM                  0x00010000
#define ASC_RET_INTEGRITY               0x00020000
#define ASC_RET_LICENSING               0x00040000

//
//  Security Credentials Attributes:
//

#define SECPKG_CRED_ATTR_NAMES 1

typedef struct _SecPkgCredentials_NamesW
{
    SEC_WCHAR SEC_FAR * sUserName;
} SecPkgCredentials_NamesW, SEC_FAR * PSecPkgCredentials_NamesW;

typedef struct _SecPkgCredentials_NamesA
{
    SEC_CHAR SEC_FAR * sUserName;
} SecPkgCredentials_NamesA, SEC_FAR * PSecPkgCredentials_NamesA;

#ifdef UNICODE
#  define SecPkgCredentials_Names SecPkgCredentials_NamesW
#  define PSecPkgCredentials_Names PSecPkgCredentials_NamesW
#else
#  define SecPkgCredentials_Names SecPkgCredentials_NamesA
#  define PSecPkgCredentials_Names PSecPkgCredentials_NamesA
#endif // !UNICODE

//
//  Security Context Attributes:
//

#define SECPKG_ATTR_SIZES           0
#define SECPKG_ATTR_NAMES           1
#define SECPKG_ATTR_LIFESPAN        2
#define SECPKG_ATTR_DCE_INFO        3
#define SECPKG_ATTR_STREAM_SIZES    4
#define SECPKG_ATTR_KEY_INFO        5
#define SECPKG_ATTR_AUTHORITY       6
#define SECPKG_ATTR_PROTO_INFO      7

typedef struct _SecPkgContext_Sizes
{
    unsigned long cbMaxToken;
    unsigned long cbMaxSignature;
    unsigned long cbBlockSize;
    unsigned long cbSecurityTrailer;
} SecPkgContext_Sizes, SEC_FAR * PSecPkgContext_Sizes;

typedef struct _SecPkgContext_StreamSizes
{
    unsigned long   cbHeader;
    unsigned long   cbTrailer;
    unsigned long   cbMaximumMessage;
    unsigned long   cBuffers;
    unsigned long   cbBlockSize;
} SecPkgContext_StreamSizes, * PSecPkgContext_StreamSizes;

typedef struct _SecPkgContext_NamesW
{
    SEC_WCHAR SEC_FAR * sUserName;
} SecPkgContext_NamesW, SEC_FAR * PSecPkgContext_NamesW;

typedef struct _SecPkgContext_NamesA
{
    SEC_CHAR SEC_FAR * sUserName;
} SecPkgContext_NamesA, SEC_FAR * PSecPkgContext_NamesA;

#ifdef UNICODE
#  define SecPkgContext_Names SecPkgContext_NamesW
#  define PSecPkgContext_Names PSecPkgContext_NamesW
#else
#  define SecPkgContext_Names SecPkgContext_NamesA
#  define PSecPkgContext_Names PSecPkgContext_NamesA
#endif // !UNICODE

typedef struct _SecPkgContext_Lifespan
{
    TimeStamp tsStart;
    TimeStamp tsExpiry;
} SecPkgContext_Lifespan, SEC_FAR * PSecPkgContext_Lifespan;

typedef struct _SecPkgContext_DceInfo
{
    unsigned long AuthzSvc;
    void SEC_FAR * pPac;
} SecPkgContext_DceInfo, SEC_FAR * PSecPkgContext_DceInfo;

typedef struct _SecPkgContext_KeyInfoA
{
    SEC_CHAR SEC_FAR *  sSignatureAlgorithmName;
    SEC_CHAR SEC_FAR *  sEncryptAlgorithmName;
    unsigned long       KeySize;
    unsigned long       SignatureAlgorithm;
    unsigned long       EncryptAlgorithm;
} SecPkgContext_KeyInfoA, SEC_FAR * PSecPkgContext_KeyInfoA;

typedef struct _SecPkgContext_KeyInfoW
{
    SEC_WCHAR SEC_FAR * sSignatureAlgorithmName;
    SEC_WCHAR SEC_FAR * sEncryptAlgorithmName;
    unsigned long       KeySize;
    unsigned long       SignatureAlgorithm;
    unsigned long       EncryptAlgorithm;
} SecPkgContext_KeyInfoW, SEC_FAR * PSecPkgContext_KeyInfoW;

#ifdef UNICODE
#define SecPkgContext_KeyInfo   SecPkgContext_KeyInfoW
#define PSecPkgContext_KeyInfo  PSecPkgContext_KeyInfoW
#else
#define SecPkgContext_KeyInfo   SecPkgContext_KeyInfoA
#define PSecPkgContext_KeyInfo  PSecPkgContext_KeyInfoA
#endif

typedef struct _SecPkgContext_AuthorityA
{
    SEC_CHAR SEC_FAR *  sAuthorityName;
} SecPkgContext_AuthorityA, * PSecPkgContext_AuthorityA;

typedef struct _SecPkgContext_AuthorityW
{
    SEC_WCHAR SEC_FAR * sAuthorityName;
} SecPkgContext_AuthorityW, * PSecPkgContext_AuthorityW;

#ifdef UNICODE
#define SecPkgContext_Authority SecPkgContext_AuthorityW
#define PSecPkgContext_Authority    PSecPkgContext_AuthorityW
#else
#define SecPkgContext_Authority SecPkgContext_AuthorityA
#define PSecPkgContext_Authority    PSecPkgContext_AuthorityA
#endif

typedef struct _SecPkgContext_ProtoInfoA
{
    SEC_CHAR SEC_FAR *  sProtocolName;
    unsigned long       majorVersion;
    unsigned long       minorVersion;
} SecPkgContext_ProtoInfoA, SEC_FAR * PSecPkgContext_ProtoInfoA;

typedef struct _SecPkgContext_ProtoInfoW
{
    SEC_WCHAR SEC_FAR * sProtocolName;
    unsigned long       majorVersion;
    unsigned long       minorVersion;
} SecPkgContext_ProtoInfoW, SEC_FAR * PSecPkgContext_ProtoInfoW;

#ifdef UNICODE
#define SecPkgContext_ProtoInfo   SecPkgContext_ProtoInfoW
#define PSecPkgContext_ProtoInfo  PSecPkgContext_ProtoInfoW
#else
#define SecPkgContext_ProtoInfo   SecPkgContext_ProtoInfoA
#define PSecPkgContext_ProtoInfo  PSecPkgContext_ProtoInfoA
#endif

typedef void
(SEC_ENTRY SEC_FAR * SEC_GET_KEY_FN) (
    void SEC_FAR * Arg,                 // Argument passed in
    void SEC_FAR * Principal,           // Principal ID
    unsigned long KeyVer,               // Key Version
    void SEC_FAR * SEC_FAR * Key,       // Returned ptr to key
    SECURITY_STATUS SEC_FAR * Status    // returned status
    );

SECURITY_STATUS SEC_ENTRY
AcquireCredentialsHandleW(
#if ISSP_MODE == 0                      // For Kernel mode
    PSECURITY_STRING pPrincipal,
    PSECURITY_STRING pPackage,
#else
    SEC_WCHAR SEC_FAR * pszPrincipal,   // Name of principal
    SEC_WCHAR SEC_FAR * pszPackage,     // Name of package
#endif
    unsigned long fCredentialUse,       // Flags indicating use
    void SEC_FAR * pvLogonId,           // Pointer to logon ID
    void SEC_FAR * pAuthData,           // Package specific data
    SEC_GET_KEY_FN pGetKeyFn,           // Pointer to GetKey() func
    void SEC_FAR * pvGetKeyArgument,    // Value to pass to GetKey()
    PCredHandle phCredential,           // (out) Cred Handle
    PTimeStamp ptsExpiry                // (out) Lifetime (optional)
    );

typedef SECURITY_STATUS
(SEC_ENTRY * ACQUIRE_CREDENTIALS_HANDLE_FN_W)(
#if ISSP_MODE == 0
    PSECURITY_STRING,
    PSECURITY_STRING,
#else
    SEC_WCHAR SEC_FAR *,
    SEC_WCHAR SEC_FAR *,
#endif
    unsigned long,
    void SEC_FAR *,
    void SEC_FAR *,
    SEC_GET_KEY_FN,
    void SEC_FAR *,
    PCredHandle,
    PTimeStamp);

SECURITY_STATUS SEC_ENTRY
AcquireCredentialsHandleA(
    SEC_CHAR SEC_FAR * pszPrincipal,    // Name of principal
    SEC_CHAR SEC_FAR * pszPackage,      // Name of package
    unsigned long fCredentialUse,       // Flags indicating use
    void SEC_FAR * pvLogonId,           // Pointer to logon ID
    void SEC_FAR * pAuthData,           // Package specific data
    SEC_GET_KEY_FN pGetKeyFn,           // Pointer to GetKey() func
    void SEC_FAR * pvGetKeyArgument,    // Value to pass to GetKey()
    PCredHandle phCredential,           // (out) Cred Handle
    PTimeStamp ptsExpiry                // (out) Lifetime (optional)
    );

typedef SECURITY_STATUS
(SEC_ENTRY * ACQUIRE_CREDENTIALS_HANDLE_FN_A)(
    SEC_CHAR SEC_FAR *,
    SEC_CHAR SEC_FAR *,
    unsigned long,
    void SEC_FAR *,
    void SEC_FAR *,
    SEC_GET_KEY_FN,
    void SEC_FAR *,
    PCredHandle,
    PTimeStamp);

#ifdef UNICODE
#  define AcquireCredentialsHandle AcquireCredentialsHandleW
#  define ACQUIRE_CREDENTIALS_HANDLE_FN ACQUIRE_CREDENTIALS_HANDLE_FN_W
#else
#  define AcquireCredentialsHandle AcquireCredentialsHandleA
#  define ACQUIRE_CREDENTIALS_HANDLE_FN ACQUIRE_CREDENTIALS_HANDLE_FN_A
#endif // !UNICODE

SECURITY_STATUS SEC_ENTRY
FreeCredentialsHandle(
    PCredHandle phCredential            // Handle to free
    );

typedef SECURITY_STATUS
(SEC_ENTRY * FREE_CREDENTIALS_HANDLE_FN)(
    PCredHandle );

////////////////////////////////////////////////////////////////////////
///
/// Context Management Functions
///
////////////////////////////////////////////////////////////////////////

SECURITY_STATUS SEC_ENTRY
InitializeSecurityContextW(
    PCredHandle phCredential,               // Cred to base context
    PCtxtHandle phContext,                  // Existing context (OPT)
#if ISSP_MODE == 0
    PSECURITY_STRING pTargetName,
#else
    SEC_WCHAR SEC_FAR * pszTargetName,      // Name of target
#endif
    unsigned long fContextReq,              // Context Requirements
    unsigned long Reserved1,                // Reserved, MBZ
    unsigned long TargetDataRep,            // Data rep of target
    PSecBufferDesc pInput,                  // Input Buffers
    unsigned long Reserved2,                // Reserved, MBZ
    PCtxtHandle phNewContext,               // (out) New Context handle
    PSecBufferDesc pOutput,                 // (inout) Output Buffers
    unsigned long SEC_FAR * pfContextAttr,  // (out) Context attrs
    PTimeStamp ptsExpiry                    // (out) Life span (OPT)
    );

typedef SECURITY_STATUS
(SEC_ENTRY * INITIALIZE_SECURITY_CONTEXT_FN_W)(
    PCredHandle,
    PCtxtHandle,
#if ISSP_MODE == 0
    PSECURITY_STRING,
#else
    SEC_WCHAR SEC_FAR *,
#endif
    unsigned long,
    unsigned long,
    unsigned long,
    PSecBufferDesc,
    unsigned long,
    PCtxtHandle,
    PSecBufferDesc,
    unsigned long SEC_FAR *,
    PTimeStamp);

SECURITY_STATUS SEC_ENTRY
InitializeSecurityContextA(
    PCredHandle phCredential,               // Cred to base context
    PCtxtHandle phContext,                  // Existing context (OPT)
    SEC_CHAR SEC_FAR * pszTargetName,       // Name of target
    unsigned long fContextReq,              // Context Requirements
    unsigned long Reserved1,                // Reserved, MBZ
    unsigned long TargetDataRep,            // Data rep of target
    PSecBufferDesc pInput,                  // Input Buffers
    unsigned long Reserved2,                // Reserved, MBZ
    PCtxtHandle phNewContext,               // (out) New Context handle
    PSecBufferDesc pOutput,                 // (inout) Output Buffers
    unsigned long SEC_FAR * pfContextAttr,  // (out) Context attrs
    PTimeStamp ptsExpiry                    // (out) Life span (OPT)
    );

typedef SECURITY_STATUS
(SEC_ENTRY * INITIALIZE_SECURITY_CONTEXT_FN_A)(
    PCredHandle,
    PCtxtHandle,
    SEC_CHAR SEC_FAR *,
    unsigned long,
    unsigned long,
    unsigned long,
    PSecBufferDesc,
    unsigned long,
    PCtxtHandle,
    PSecBufferDesc,
    unsigned long SEC_FAR *,
    PTimeStamp);

#ifdef UNICODE
#  define InitializeSecurityContext InitializeSecurityContextW
#  define INITIALIZE_SECURITY_CONTEXT_FN INITIALIZE_SECURITY_CONTEXT_FN_W
#else
#  define InitializeSecurityContext InitializeSecurityContextA
#  define INITIALIZE_SECURITY_CONTEXT_FN INITIALIZE_SECURITY_CONTEXT_FN_A
#endif // !UNICODE

SECURITY_STATUS SEC_ENTRY
AcceptSecurityContext(
    PCredHandle phCredential,               // Cred to base context
    PCtxtHandle phContext,                  // Existing context (OPT)
    PSecBufferDesc pInput,                  // Input buffer
    unsigned long fContextReq,              // Context Requirements
    unsigned long TargetDataRep,            // Target Data Rep
    PCtxtHandle phNewContext,               // (out) New context handle
    PSecBufferDesc pOutput,                 // (inout) Output buffers
    unsigned long SEC_FAR * pfContextAttr,  // (out) Context attributes
    PTimeStamp ptsExpiry                    // (out) Life span (OPT)
    );

typedef SECURITY_STATUS
(SEC_ENTRY * ACCEPT_SECURITY_CONTEXT_FN)(
    PCredHandle,
    PCtxtHandle,
    PSecBufferDesc,
    unsigned long,
    unsigned long,
    PCtxtHandle,
    PSecBufferDesc,
    unsigned long SEC_FAR *,
    PTimeStamp);

SECURITY_STATUS SEC_ENTRY
CompleteAuthToken(
    PCtxtHandle phContext,              // Context to complete
    PSecBufferDesc pToken               // Token to complete
    );

typedef SECURITY_STATUS
(SEC_ENTRY * COMPLETE_AUTH_TOKEN_FN)(
    PCtxtHandle,
    PSecBufferDesc);

SECURITY_STATUS SEC_ENTRY
ImpersonateSecurityContext(
    PCtxtHandle phContext               // Context to impersonate
    );

typedef SECURITY_STATUS
(SEC_ENTRY * IMPERSONATE_SECURITY_CONTEXT_FN)(
    PCtxtHandle);

SECURITY_STATUS SEC_ENTRY
RevertSecurityContext(
    PCtxtHandle phContext               // Context from which to re
    );

typedef SECURITY_STATUS
(SEC_ENTRY * REVERT_SECURITY_CONTEXT_FN)(
    PCtxtHandle);

SECURITY_STATUS SEC_ENTRY
QuerySecurityContextToken(
    PCtxtHandle phContext,
    void SEC_FAR * Token
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_SECURITY_CONTEXT_TOKEN_FN)(
    PCtxtHandle, void SEC_FAR *);

SECURITY_STATUS SEC_ENTRY
DeleteSecurityContext(
    PCtxtHandle phContext               // Context to delete
    );

typedef SECURITY_STATUS
(SEC_ENTRY * DELETE_SECURITY_CONTEXT_FN)(
    PCtxtHandle);

SECURITY_STATUS SEC_ENTRY
ApplyControlToken(
    PCtxtHandle phContext,              // Context to modify
    PSecBufferDesc pInput               // Input token to apply
    );

typedef SECURITY_STATUS
(SEC_ENTRY * APPLY_CONTROL_TOKEN_FN)(
    PCtxtHandle, PSecBufferDesc);

SECURITY_STATUS SEC_ENTRY
QueryContextAttributesW(
    PCtxtHandle phContext,              // Context to query
    unsigned long ulAttribute,          // Attribute to query
    void SEC_FAR * pBuffer              // Buffer for attributes
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_CONTEXT_ATTRIBUTES_FN_W)(
    PCtxtHandle,
    unsigned long,
    void SEC_FAR *);

SECURITY_STATUS SEC_ENTRY
QueryContextAttributesA(
    PCtxtHandle phContext,              // Context to query
    unsigned long ulAttribute,          // Attribute to query
    void SEC_FAR * pBuffer              // Buffer for attributes
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_CONTEXT_ATTRIBUTES_FN_A)(
    PCtxtHandle,
    unsigned long,
    void SEC_FAR *);

#ifdef UNICODE
#  define QueryContextAttributes QueryContextAttributesW
#  define QUERY_CONTEXT_ATTRIBUTES_FN QUERY_CONTEXT_ATTRIBUTES_FN_W
#else
#  define QueryContextAttributes QueryContextAttributesA
#  define QUERY_CONTEXT_ATTRIBUTES_FN QUERY_CONTEXT_ATTRIBUTES_FN_A
#endif // !UNICODE

SECURITY_STATUS SEC_ENTRY
QueryCredentialsAttributesW(
    PCredHandle phCredential,              // Credential to query
    unsigned long ulAttribute,          // Attribute to query
    void SEC_FAR * pBuffer              // Buffer for attributes
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_CREDENTIALS_ATTRIBUTES_FN_W)(
    PCredHandle,
    unsigned long,
    void SEC_FAR *);

SECURITY_STATUS SEC_ENTRY
QueryCredentialsAttributesA(
    PCredHandle phCredential,              // Credential to query
    unsigned long ulAttribute,          // Attribute to query
    void SEC_FAR * pBuffer              // Buffer for attributes
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_CREDENTIALS_ATTRIBUTES_FN_A)(
    PCredHandle,
    unsigned long,
    void SEC_FAR *);

#ifdef UNICODE
#  define QueryCredentialsAttributes QueryCredentialsAttributesW
#  define QUERY_CREDENTIALS_ATTRIBUTES_FN QUERY_CREDENTIALS_ATTRIBUTES_FN_W
#else
#  define QueryCredentialsAttributes QueryCredentialsAttributesA
#  define QUERY_CREDENTIALS_ATTRIBUTES_FN QUERY_CREDENTIALS_ATTRIBUTES_FN_A
#endif // !UNICODE

SECURITY_STATUS SEC_ENTRY
FreeContextBuffer(
    void SEC_FAR * pvContextBuffer      // buffer to free
    );

typedef SECURITY_STATUS
(SEC_ENTRY * FREE_CONTEXT_BUFFER_FN)(
    void SEC_FAR *);

///////////////////////////////////////////////////////////////////
////
////    Message Support API
////
//////////////////////////////////////////////////////////////////

SECURITY_STATUS SEC_ENTRY
MakeSignature(
    PCtxtHandle phContext,              // Context to use
    unsigned long fQOP,                 // Quality of Protection
    PSecBufferDesc pMessage,            // Message to sign
    unsigned long MessageSeqNo          // Message Sequence Num.
    );

typedef SECURITY_STATUS
(SEC_ENTRY * MAKE_SIGNATURE_FN)(
    PCtxtHandle,
    unsigned long,
    PSecBufferDesc,
    unsigned long);

SECURITY_STATUS SEC_ENTRY
VerifySignature(
    PCtxtHandle phContext,              // Context to use
    PSecBufferDesc pMessage,            // Message to verify
    unsigned long MessageSeqNo,         // Sequence Num.
    unsigned long SEC_FAR * pfQOP       // QOP used
    );

typedef SECURITY_STATUS
(SEC_ENTRY * VERIFY_SIGNATURE_FN)(
    PCtxtHandle,
    PSecBufferDesc,
    unsigned long,
    unsigned long SEC_FAR *);

///////////////////////////////////////////////////////////////////////////

////
////    Misc.
////
///////////////////////////////////////////////////////////////////////////

SECURITY_STATUS SEC_ENTRY
EnumerateSecurityPackagesW(
    unsigned long SEC_FAR * pcPackages,     // Receives num. packages
    PSecPkgInfoW SEC_FAR * ppPackageInfo    // Receives array of info
    );

typedef SECURITY_STATUS
(SEC_ENTRY * ENUMERATE_SECURITY_PACKAGES_FN_W)(
    unsigned long SEC_FAR *,
    PSecPkgInfoW SEC_FAR *);

SECURITY_STATUS SEC_ENTRY
EnumerateSecurityPackagesA(
    unsigned long SEC_FAR * pcPackages,     // Receives num. packages
    PSecPkgInfoA SEC_FAR * ppPackageInfo    // Receives array of info
    );

typedef SECURITY_STATUS
(SEC_ENTRY * ENUMERATE_SECURITY_PACKAGES_FN_A)(
    unsigned long SEC_FAR *,
    PSecPkgInfoA SEC_FAR *);

#ifdef UNICODE
#  define EnumerateSecurityPackages EnumerateSecurityPackagesW
#  define ENUMERATE_SECURITY_PACKAGES_FN ENUMERATE_SECURITY_PACKAGES_FN_W
#else
#  define EnumerateSecurityPackages EnumerateSecurityPackagesA
#  define ENUMERATE_SECURITY_PACKAGES_FN ENUMERATE_SECURITY_PACKAGES_FN_A
#endif // !UNICODE

SECURITY_STATUS SEC_ENTRY
QuerySecurityPackageInfoW(
#if ISSP_MODE == 0
    PSECURITY_STRING pPackageName,
#else
    SEC_WCHAR SEC_FAR * pszPackageName,     // Name of package
#endif
    PSecPkgInfoW SEC_FAR *ppPackageInfo              // Receives package info
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_SECURITY_PACKAGE_INFO_FN_W)(
#if ISSP_MODE == 0
    PSECURITY_STRING,
#else
    SEC_WCHAR SEC_FAR *,
#endif
    PSecPkgInfoW SEC_FAR *);

SECURITY_STATUS SEC_ENTRY
QuerySecurityPackageInfoA(
    SEC_CHAR SEC_FAR * pszPackageName,      // Name of package
    PSecPkgInfoA SEC_FAR *ppPackageInfo              // Receives package info
    );

typedef SECURITY_STATUS
(SEC_ENTRY * QUERY_SECURITY_PACKAGE_INFO_FN_A)(
    SEC_CHAR SEC_FAR *,
    PSecPkgInfoA SEC_FAR *);

#ifdef UNICODE
#  define QuerySecurityPackageInfo QuerySecurityPackageInfoW
#  define QUERY_SECURITY_PACKAGE_INFO_FN QUERY_SECURITY_PACKAGE_INFO_FN_W
#else
#  define QuerySecurityPackageInfo QuerySecurityPackageInfoA
#  define QUERY_SECURITY_PACKAGE_INFO_FN QUERY_SECURITY_PACKAGE_INFO_FN_A
#endif // !UNICODE

#if ISSP_MODE == 0

//
// Deferred mode calls for rdr
//

SECURITY_STATUS SEC_ENTRY
DeleteSecurityContextDefer(
    PCtxtHandle     phContext);

SECURITY_STATUS SEC_ENTRY
FreeCredentialsHandleDefer(
    PCredHandle     phCreds);

#endif

typedef enum _SecDelegationType {
    SecFull,
    SecService,
    SecTree,
    SecDirectory,
    SecObject
} SecDelegationType, * PSecDelegationType;

SECURITY_STATUS SEC_ENTRY
DelegateSecurityContext(
    PCtxtHandle         phContext,          // IN Active context to delegate
#if ISSP_MODE == 0
    PSECURITY_STRING    pTarget,            // IN Target path
#else
    SEC_CHAR SEC_FAR *  pszTarget,
#endif
    SecDelegationType   DelegationType,     // IN Type of delegation
    PTimeStamp          pExpiry,            // IN OPTIONAL time limit
    PSecBuffer          pPackageParameters, // IN OPTIONAL package specific
    PSecBufferDesc      pOutput);           // OUT Token for applycontroltoken.

///////////////////////////////////////////////////////////////////////////
////
////    Proxies
////
///////////////////////////////////////////////////////////////////////////

//
// Proxies are only available on NT platforms
//

#ifdef NT_INCLUDED

typedef enum _SSPI_PROXY_CLASS_TAG {
        SspiProxyFull,
        SspiProxyService,
        SspiProxyTree,
        SspiProxyDirectory
} _SSPI_PROXY_CLASS;

#ifndef SSPI_PROXY_CLASS
#define SSPI_PROXY_CLASS _SSPI_PROXY_CLASS
#endif

#ifndef _DWORD_DEFINED
#define _DWORD_DEFINED
typedef unsigned long DWORD;
#endif // !_DWORD_DEFINED

#include <accctrl.h>

//
// proxy access rights
//
#define PROXY_READ              0x01        // reading of proxy data
#define PROXY_WRITE             0x02        // writing of proxy data
#define PROXY_INVOKE            0x04        // invoking an existing proxy

#define PROXY_ALL_ACCESS        ( READ_CONTROL | WRITE_DAC | DELETE | 0x07 )

#define PROXY_GENERIC_READ      PROXY_READ
#define PROXY_GENERIC_WRITE     PROXY_WRITE
#define PROXY_GENERIC_EXECUTE   PROXY_INVOKE
#define PROXY_GENERIC_ALL       ( PROXY_READ   |  \
                                  PROXY_WRITE  |  \
                                  PROXY_INVOKE |  \
                                  WRITE_DAC    |  \
                                  READ_CONTROL |  \
                                  DELETE )

#define GRANTOR_DEFAULT_ACCESS  ( WRITE_DAC    |  \
                                  READ_CONTROL |  \
                                  DELETE       |  \
                                  PROXY_READ   |  \
                                  PROXY_WRITE  )

#define GRANTEE_DEFAULT_ACCESS  ( PROXY_INVOKE )

#define ADMIN_DEFAULT_ACCESS    ( WRITE_DAC    |  \
                                  READ_CONTROL |  \
                                  DELETE       |  \
                                  PROXY_READ  )

//
// Types available for use with proxy APIs
//
typedef enum _SECURITY_INFORMATION_TYPE {
    GranteeList,                // simple list of grantees' DN
    ExplicitAccess,             // list of EXPLICIT_ACCESSes
    SecurityDescriptor          // SECURITY_DESCRIPTOR
} SECURITY_INFORMATION_TYPE;

typedef struct _SECURITY_ACCESS_INFO {
    SECURITY_INFORMATION_TYPE    ulType;
    VOID SEC_FAR *               pvData;
} SECURITY_ACCESS_INFO, SEC_FAR * PSECURITY_ACCESS_INFO;

typedef struct _PROXY_ACCESS_LIST {
    ULONG                       cAccesses;
    EXPLICIT_ACCESS SEC_FAR *   pAccesses;
} PROXY_ACCESS_LIST, SEC_FAR * PPROXY_ACCESS_LIST;

typedef struct _PROXY_GRANTEE_LIST {
    ULONG                          cGrantees;
#if ISSP_MODE == 0
    PSECURITY_STRING *             ppssGrantees;
#else
    SEC_WCHAR SEC_FAR * SEC_FAR *  ppwszGrantees;
#endif
} PROXY_GRANTEE_LIST, SEC_FAR * PPROXY_GRANTEE_LIST;

typedef struct _PROXY_REFERENCE {
    GUID    gIssuingDomain;
    GUID    gProxyId;
} PROXY_REFERENCE, SEC_FAR * PPROXY_REFERENCE;

//
// GrantProxy API
//
SECURITY_STATUS SEC_ENTRY
GrantProxyW(
    PCredHandle           phCredential,           // (in) Handle to base proxy on
#if ISSP_MODE == 0
    PSECURITY_STRING      pssProxyName,           // (in optional) proxy name
#else
    SEC_WCHAR SEC_FAR *   pwszProxyName,
#endif
    SSPI_PROXY_CLASS           ProxyClass,             // (in) class requested
#if ISSP_MODE == 0
    PSECURITY_STRING      pssTarget,
#else
    SEC_WCHAR SEC_FAR *   pwszTarget,             // (in) Target of proxy
#endif
    unsigned long         ContainerMask,          // (in) Access mask
    unsigned long         ObjectMask,             // (in) Access mask
    PTimeStamp            tsExpiry,               // (in) time proxy expires
    PSECURITY_ACCESS_INFO pAccessInfo,            // (in) grantees and accesses
    PPROXY_REFERENCE      phProxy                 // (out) proxy handle
    );

typedef SECURITY_STATUS
(SEC_ENTRY * GRANT_PROXY_FN_W)(
    PCredHandle,
#if ISSP_MODE == 0
    PSECURITY_STRING,
#else
    SEC_WCHAR SEC_FAR *,
#endif
    SSPI_PROXY_CLASS,
#if ISSP_MODE == 0
    PSECURITY_STRING,
#else
    SEC_WCHAR SEC_FAR *,
#endif
    unsigned long,
    unsigned long,
    PTimeStamp,
    PSECURITY_ACCESS_INFO,
    PPROXY_REFERENCE );

SECURITY_STATUS SEC_ENTRY
GrantProxyA(
    PCredHandle           phCredential,           // (in) Handle to base proxy on
    SEC_CHAR SEC_FAR *    pszProxyName,           // (in optional) proxy name
    SSPI_PROXY_CLASS           ProxyClass,             // (in) class requested
    SEC_CHAR SEC_FAR *    pszTarget,              // (in) Target of proxy
    unsigned long         ContainerMask,          // (in) Access mask
    unsigned long         ObjectMask,             // (in) Access mask
    PTimeStamp            ptsExpiry,              // (in) time proxy expires
    PSECURITY_ACCESS_INFO pAccessInfo,            // (in) grantees and accesses
    PPROXY_REFERENCE      phProxy                 // (out) proxy handle
    );

typedef SECURITY_STATUS
(SEC_ENTRY * GRANT_PROXY_FN_A)(
    PCredHandle,
    SEC_CHAR SEC_FAR *,
    SSPI_PROXY_CLASS,
    SEC_CHAR SEC_FAR *,
    unsigned long,
    unsigned long,
    PTimeStamp,
    PSECURITY_ACCESS_INFO,
    PPROXY_REFERENCE );

#ifdef UNICODE
#  define GrantProxy       GrantProxyW
#  define GRANT_PROXY_FN   GRANT_PROXY_FN_W
#else
#  define GrantProxy       GrantProxyA
#  define GRANT_PROXY_FN   GRANT_PROXY_FN_A
#endif // !UNICODE

//
// RevokeProxy API
//
SECURITY_STATUS SEC_ENTRY
RevokeProxyW(
    PCredHandle          phCredential,           // (in) credentials
    PPROXY_REFERENCE     phProxy,                // (in) proxy handle
#if ISSP_MODE == 0
    PSECURITY_STRING     pssProxyName
#else
    SEC_WCHAR SEC_FAR *  pwszProxyName           // (in optional) Proxy name
#endif
    );

typedef SECURITY_STATUS
(SEC_ENTRY * REVOKE_PROXY_FN_W)(
    PCredHandle,
    PPROXY_REFERENCE,
#if ISSP_MODE == 0
    PSECURITY_STRING
#else
    SEC_WCHAR SEC_FAR *
#endif
    );

SECURITY_STATUS SEC_ENTRY
RevokeProxyA(
    PCredHandle          phCredential,           // (in) credentials
    PPROXY_REFERENCE     phProxy,                // (in) proxy handle
    SEC_CHAR SEC_FAR *   pszProxyName            // (in) proxy name
    );

typedef SECURITY_STATUS
(SEC_ENTRY * REVOKE_PROXY_FN_A)(
    PCredHandle,
    PPROXY_REFERENCE,
    SEC_CHAR SEC_FAR *
    );

#ifdef UNICODE
#  define RevokeProxy      RevokeProxyW
#  define REVOKE_PROXY_FN  REVOKE_PROXY_FN_W
#else
#  define RevokeProxy      RevokeProxyA
#  define REVOKE_PROXY_FN  REVOKE_PROXY_FN_A
#endif // !UNICODE

//
// InvokeProxy API
//
SECURITY_STATUS SEC_ENTRY
InvokeProxyW(
    PCredHandle          phCredential,           // (in) handle to base proxy on
    PPROXY_REFERENCE     phProxy,                // (in) proxy handle
#if ISSP_MODE == 0
    PSECURITY_STRING     pssProxyName,
#else
    SEC_WCHAR SEC_FAR *  pwszProxyName,          // (in optional) Proxy name
#endif
    PCtxtHandle          phContext               // (out) security context
    );

typedef SECURITY_STATUS
(SEC_ENTRY * INVOKE_PROXY_FN_W)(
    PCredHandle,
    PPROXY_REFERENCE,
#if ISSP_MODE == 0
    PSECURITY_STRING,
#else
    SEC_WCHAR SEC_FAR *,
#endif
    PCtxtHandle );

SECURITY_STATUS SEC_ENTRY
InvokeProxyA(
    PCredHandle          phCredential,           // (in) handle to base proxy on
    PPROXY_REFERENCE     phProxy,                // (in) proxy handle
    SEC_CHAR SEC_FAR *   pszProxyName,           // (in optional) Proxy name
    PCtxtHandle          phContext               // (out) security context
    );

typedef SECURITY_STATUS
(SEC_ENTRY * INVOKE_PROXY_FN_A)(
    PCredHandle,
    PPROXY_REFERENCE,
    SEC_CHAR SEC_FAR *,
    PCtxtHandle );

#ifdef UNICODE
#  define InvokeProxy      InvokeProxyW
#  define INVOKE_PROXY_FN  INVOKE_PROXY_FN_W
#else
#  define InvokeProxy      InvokeProxyA
#  define INVOKE_PROXY_FN  INVOKE_PROXY_FN_A
#endif // !UNICODE

//
// RenewProxy API
//
SECURITY_STATUS SEC_ENTRY
RenewProxyW(
    PCredHandle          phCredential,           // (in) credentials
    PPROXY_REFERENCE     phProxy,                // (in) proxy handle
#if ISSP_MODE == 0
    PSECURITY_STRING     pssProxyName,
#else
    SEC_WCHAR SEC_FAR *  pwszProxyName,          // (in) proxy name
#endif
    PTimeStamp           ptsExpiry               // (in) new absolute expiry
    );

typedef SECURITY_STATUS
(SEC_ENTRY * RENEW_PROXY_FN_W)(
    PCredHandle,
    PPROXY_REFERENCE,
#if ISSP_MODE == 0
    PSECURITY_STRING,
#else
    SEC_WCHAR SEC_FAR *,
#endif
    PTimeStamp
    );

SECURITY_STATUS SEC_ENTRY
RenewProxyA(
    PCredHandle          phCredential,           // (in) credentials
    PPROXY_REFERENCE     phProxy,                // (in) proxy handle
    SEC_CHAR SEC_FAR *   pszProxyName,           // (in) proxy name
    PTimeStamp           ptsExpiry               // (in) new expiry time
    );

typedef SECURITY_STATUS
(SEC_ENTRY * RENEW_PROXY_FN_A)(
    PCredHandle,
    PPROXY_REFERENCE,
    SEC_CHAR SEC_FAR *,
    PTimeStamp
    );

#ifdef UNICODE
#  define RenewProxy      RenewProxyW
#  define RENEW_PROXY_FN  RENEW_PROXY_FN_W
#else
#  define RenewProxy      RenewProxyA
#  define RENEW_PROXY_FN  RENEW_PROXY_FN_A
#endif // !UNICODE

#endif // NT_INCLUDED

///////////////////////////////////////////////////////////////////////////////
////
////  Fast access for RPC:
////
///////////////////////////////////////////////////////////////////////////////

#define SECURITY_ENTRYPOINTW SEC_TEXT("InitSecurityInterfaceW")
#define SECURITY_ENTRYPOINTA SEC_TEXT("InitSecurityInterfaceA")
#define SECURITY_ENTRYPOINT16 "INITSECURITYINTERFACEA"

#ifdef SECURITY_WIN32
#  ifdef UNICODE
#    define SECURITY_ENTRYPOINT SECURITY_ENTRYPOINTW
#  else // UNICODE
#    define SECURITY_ENTRYPOINT SECURITY_ENTRYPOINTA
#  endif // UNICODE
#else // SECURITY_WIN32
#  define SECURITY_ENTRYPOINT SECURITY_ENTRYPOINT16
#endif // SECURITY_WIN32

typedef struct _SECURITY_FUNCTION_TABLE_W {
    unsigned long                       dwVersion;
    ENUMERATE_SECURITY_PACKAGES_FN_W    EnumerateSecurityPackagesW;
    void SEC_FAR *                      Reserved1;
//    QUERY_CREDENTIALS_ATTRIBUTES_FN_W   QueryCredentialsAttributesW;
    ACQUIRE_CREDENTIALS_HANDLE_FN_W     AcquireCredentialsHandleW;
    FREE_CREDENTIALS_HANDLE_FN          FreeCredentialHandle;
    void SEC_FAR *                      Reserved2;
    INITIALIZE_SECURITY_CONTEXT_FN_W    InitializeSecurityContextW;
    ACCEPT_SECURITY_CONTEXT_FN          AcceptSecurityContext;
    COMPLETE_AUTH_TOKEN_FN              CompleteAuthToken;
    DELETE_SECURITY_CONTEXT_FN          DeleteSecurityContext;
    APPLY_CONTROL_TOKEN_FN              ApplyControlToken;
    QUERY_CONTEXT_ATTRIBUTES_FN_W       QueryContextAttributesW;
    IMPERSONATE_SECURITY_CONTEXT_FN     ImpersonateSecurityContext;
    REVERT_SECURITY_CONTEXT_FN          RevertSecurityContext;
    MAKE_SIGNATURE_FN                   MakeSignature;
    VERIFY_SIGNATURE_FN                 VerifySignature;
    FREE_CONTEXT_BUFFER_FN              FreeContextBuffer;
    QUERY_SECURITY_PACKAGE_INFO_FN_W    QuerySecurityPackageInfoW;
    void SEC_FAR *                      Reserved3;
    void SEC_FAR *                      Reserved4;
#ifdef NT_INCLUDED
    GRANT_PROXY_FN_W                    GrantProxyW;
    REVOKE_PROXY_FN_W                   RevokeProxy;
    INVOKE_PROXY_FN_W                   InvokeProxy;
    RENEW_PROXY_FN_W                    RenewProxy;
#else
    void SEC_FAR *                      GrantProxyW;
    void SEC_FAR *                      RevokeProxy;
    void SEC_FAR *                      InvokeProxy;
    void SEC_FAR *                      RenewProxy;
#endif
    QUERY_SECURITY_CONTEXT_TOKEN_FN     QuerySecurityContextToken;
} SecurityFunctionTableW, SEC_FAR * PSecurityFunctionTableW;

typedef struct _SECURITY_FUNCTION_TABLE_A {
    unsigned long                       dwVersion;
    ENUMERATE_SECURITY_PACKAGES_FN_A    EnumerateSecurityPackagesA;
    void SEC_FAR *                      Reserved1;
//    QUERY_CREDENTIALS_ATTRIBUTES_FN_A   QueryCredentialsAttributesA;
    ACQUIRE_CREDENTIALS_HANDLE_FN_A     AcquireCredentialsHandleA;
    FREE_CREDENTIALS_HANDLE_FN          FreeCredentialHandle;
    void SEC_FAR *                      Reserved2;
    INITIALIZE_SECURITY_CONTEXT_FN_A    InitializeSecurityContextA;
    ACCEPT_SECURITY_CONTEXT_FN          AcceptSecurityContext;
    COMPLETE_AUTH_TOKEN_FN              CompleteAuthToken;
    DELETE_SECURITY_CONTEXT_FN          DeleteSecurityContext;
    APPLY_CONTROL_TOKEN_FN              ApplyControlToken;
    QUERY_CONTEXT_ATTRIBUTES_FN_A       QueryContextAttributesA;
    IMPERSONATE_SECURITY_CONTEXT_FN     ImpersonateSecurityContext;
    REVERT_SECURITY_CONTEXT_FN          RevertSecurityContext;
    MAKE_SIGNATURE_FN                   MakeSignature;
    VERIFY_SIGNATURE_FN                 VerifySignature;
    FREE_CONTEXT_BUFFER_FN              FreeContextBuffer;
    QUERY_SECURITY_PACKAGE_INFO_FN_A    QuerySecurityPackageInfoA;
    void SEC_FAR *                      Reserved3;
    void SEC_FAR *                      Reserved4;
#ifdef NT_INCLUDED
    GRANT_PROXY_FN_A                    GrantProxyA;
    REVOKE_PROXY_FN_A                   RevokeProxy;
    INVOKE_PROXY_FN_A                   InvokeProxy;
    RENEW_PROXY_FN_A                    RenewProxy;
#else
    void SEC_FAR *                      GrantProxyA;
    void SEC_FAR *                      RevokeProxy;
    void SEC_FAR *                      InvokeProxy;
    void SEC_FAR *                      RenewProxy;
#endif
    QUERY_SECURITY_CONTEXT_TOKEN_FN     QuerySecurityContextToken;
} SecurityFunctionTableA, SEC_FAR * PSecurityFunctionTableA;

#ifdef UNICODE
#  define SecurityFunctionTable SecurityFunctionTableW
#  define PSecurityFunctionTable PSecurityFunctionTableW
#else
#  define SecurityFunctionTable SecurityFunctionTableA
#  define PSecurityFunctionTable PSecurityFunctionTableA
#endif // !UNICODE

#define SECURITY_

#define SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION     1

PSecurityFunctionTableA SEC_ENTRY
InitSecurityInterfaceA(
    void
    );

typedef PSecurityFunctionTableA
(SEC_ENTRY * INIT_SECURITY_INTERFACE_A)(void);

PSecurityFunctionTableW SEC_ENTRY
InitSecurityInterfaceW(
    void
    );

typedef PSecurityFunctionTableW
(SEC_ENTRY * INIT_SECURITY_INTERFACE_W)(void);

#ifdef UNICODE
#  define InitSecurityInterface InitSecurityInterfaceW
#  define INIT_SECURITY_INTERFACE INIT_SECURITY_INTERFACE_W
#else
#  define InitSecurityInterface InitSecurityInterfaceA
#  define INIT_SECURITY_INTERFACE INIT_SECURITY_INTERFACE_A
#endif // !UNICODE

SECURITY_STATUS
SEC_ENTRY
AddSecurityPackageA(
    SEC_CHAR SEC_FAR *  pszPackageName,
    void     SEC_FAR *  Reserved
    );

SECURITY_STATUS
SEC_ENTRY
AddSecurityPackageW(
    SEC_WCHAR SEC_FAR * pszPackageName,
    void      SEC_FAR * Reserved
    );

#ifdef UNICODE
#define AddSecurityPackage  AddSecurityPackageW
#else
#define AddSecurityPackage  AddSecurityPackageA
#endif

SECURITY_STATUS
SEC_ENTRY
DeleteSecurityPackageA(
    SEC_CHAR SEC_FAR *  pszPackageName );

SECURITY_STATUS
SEC_ENTRY
DeleteSecurityPackageW(
    SEC_WCHAR SEC_FAR * pszPackageName );

#ifdef UNICODE
#define DeleteSecurityPackage   DeleteSecurityPackageW
#else
#define DeleteSecurityPackage   DeleteSecurityPackageA
#endif

#ifdef SECURITY_DOS
#pragma warning(default:4147)
#endif

#endif // __SSPI_H__


STUBS.C

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1994
//
// File:        stubs.cxx
//
// Contents:    user-mode stubs for security API
//
//
//------------------------------------------------------------------------
#include "sampssp.h"

SecurityFunctionTableW SecTableW = {SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION,
                                    EnumerateSecurityPackagesW,
                                    NULL,
                                    AcquireCredentialsHandleW,
                                    FreeCredentialsHandle,
                                    NULL, // LogonUser
                                    InitializeSecurityContextW,
                                    AcceptSecurityContext,
                                    CompleteAuthToken,
                                    DeleteSecurityContext,
                                    ApplyControlToken,
                                    QueryContextAttributesW,
                                    ImpersonateSecurityContext,
                                    RevertSecurityContext,
                                    MakeSignature,
                                    VerifySignature,
                                    FreeContextBuffer,
                                    QuerySecurityPackageInfoW
                                   };

PSEC_CONTEXT ContextList;
PCREDENTIAL CredentialList;
ULONG NextId;
TimeStamp Forever = {0x7fffffff,0xfffffff};
TimeStamp Never = {0,0};

//+-------------------------------------------------------------------------
//
//  Function:   LocateContext
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

PSEC_CONTEXT
LocateContext(ULONG ContextId)
{
    PSEC_CONTEXT TestContext;
    EnterCriticalSection(&DllCritSect);

    TestContext = ContextList;
    while (TestContext != NULL)
    {
        if (TestContext->ContextId == ContextId)
        {
            break;
        }
        TestContext = TestContext->Next;
    }
    LeaveCriticalSection(&DllCritSect);
    return(TestContext);

}

//+-------------------------------------------------------------------------
//
//  Function:   DeleteContext
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

BOOLEAN
DeleteContext(ULONG ContextId)
{
    PSEC_CONTEXT TestContext, LastContext;

    EnterCriticalSection(&DllCritSect);
    TestContext = ContextList;
    LastContext = NULL;

    while (TestContext != NULL)
    {
        if (TestContext->ContextId == ContextId)
        {
            break;
        }
        LastContext = TestContext;
        TestContext = TestContext->Next;
    }
    if (TestContext != NULL)
    {
        if (LastContext != NULL)
        {
            LastContext->Next = TestContext->Next;
        }
        else
        {
            assert(ContextList == TestContext);
            ContextList = TestContext->Next;
        }
        LocalFree(TestContext);
    }
    LeaveCriticalSection(&DllCritSect);
    return( TestContext == NULL ? FALSE : TRUE );
}

//+-------------------------------------------------------------------------
//
//  Function:   AddContext
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

VOID
AddContext(PSEC_CONTEXT Context)
{
    EnterCriticalSection(&DllCritSect);
    Context->Next = ContextList;
    ContextList = Context;
    LeaveCriticalSection(&DllCritSect);
}

//+-------------------------------------------------------------------------
//
//  Function:   InitSecurityInterfaceW
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

PCREDENTIAL
LocateCredential(ULONG CredentialId)
{
    PCREDENTIAL TestCredential;
    EnterCriticalSection(&DllCritSect);

    TestCredential = CredentialList;
    while (TestCredential != NULL)
    {
        if (TestCredential->CredentialId == CredentialId)
        {
            break;
        }
        TestCredential = TestCredential->Next;
    }
    LeaveCriticalSection(&DllCritSect);
    return(TestCredential);

}

BOOLEAN
DeleteCredential(ULONG CredentialId)
{
    PCREDENTIAL TestCredential, LastCredential;

    EnterCriticalSection(&DllCritSect);
    TestCredential = CredentialList;
    LastCredential = NULL;

    while (TestCredential != NULL)
    {
        if (TestCredential->CredentialId == CredentialId)
        {
            break;
        }
        LastCredential = TestCredential;
        TestCredential = TestCredential->Next;
    }
    if (TestCredential != NULL)
    {
        if (LastCredential != NULL)
        {
            LastCredential->Next = TestCredential->Next;
        }
        else
        {
            assert(CredentialList == TestCredential);
            CredentialList = TestCredential->Next;
        }
        LocalFree(TestCredential);
    }
    LeaveCriticalSection(&DllCritSect);
    return( TestCredential == NULL ? FALSE : TRUE );
}

VOID
AddCredential(PCREDENTIAL Credential)
{
    EnterCriticalSection(&DllCritSect);
    Credential->Next = CredentialList;
    CredentialList = Credential;
    LeaveCriticalSection(&DllCritSect);
}

ULONG
GetNewId()
{
    ULONG NewId;

    EnterCriticalSection(&DllCritSect);
    NewId = NextId;
    NextId ++;
    LeaveCriticalSection(&DllCritSect);
    return(NewId);

}

PSecBuffer
LocateBuffer(PSecBufferDesc Buffer, ULONG MinimumSize)
{
    ULONG Index;
    if (Buffer == NULL)
    {
        return(NULL);
    }

    for (Index = 0; Index < Buffer->cBuffers  ; Index++)
    {
        if (((Buffer->pBuffers[Index].BufferType) & ~SECBUFFER_ATTRMASK)
            == SECBUFFER_TOKEN)
        {

            //
            // Do size checking
            //

            if (Buffer->pBuffers[Index].cbBuffer < MinimumSize)
            {
                return(NULL);
            }
            return(&Buffer->pBuffers[Index]);
        }
    }
    return(NULL);
}

PSecBuffer
LocateSecBuffer(PSecBufferDesc Buffer)
{
    return(LocateBuffer(Buffer, sizeof(MESSAGE)));
}

PSecBuffer
LocateSigBuffer(PSecBufferDesc Buffer)
{
    return(LocateBuffer(Buffer, sizeof(SIGNATURE)));
}

//+-------------------------------------------------------------------------
//
//  Function:   InitSecurityInterfaceW
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------
PSecurityFunctionTableW SEC_ENTRY
InitSecurityInterfaceW(VOID)
{
    InitializeCriticalSection( &DllCritSect );
    return(&SecTableW);
}

//+-------------------------------------------------------------------------
//
//  Function:   AcquireCredentialsHandleW
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
AcquireCredentialsHandleW(
    LPWSTR                      pszPrincipal,       // Name of principal
    LPWSTR                      pszPackageName,     // Name of package
    unsigned long               fCredentialUse,     // Flags indicating use
    void SEC_FAR *              pvLogonId,          // Pointer to logon ID
    void SEC_FAR *              pAuthData,          // Package specific data
    SEC_GET_KEY_FN              pGetKeyFn,          // Pointer to GetKey() func
    void SEC_FAR *              pvGetKeyArgument,   // Value to pass to GetKey()
    PCredHandle                 phCredential,       // (out) Cred Handle
    PTimeStamp                  ptsExpiry           // (out) Lifetime (optional)
    )
{
    PCREDENTIAL NewCredential = NULL;

    if ((fCredentialUse & (SECPKG_CRED_BOTH)) == 0)
    {
        return(SEC_E_UNKNOWN_CREDENTIALS);
    }
    NewCredential = (PCREDENTIAL) LocalAlloc(0,sizeof(CREDENTIAL));
    if (NewCredential == NULL)
    {
        return(SEC_E_INSUFFICIENT_MEMORY);
    }
    NewCredential->CredentialId = GetNewId();
    NewCredential->Use = fCredentialUse;
    NewCredential->AuthData = *(ULONG *)pAuthData;
    phCredential->dwUpper = NewCredential->CredentialId;
    *ptsExpiry = Forever;
    AddCredential(NewCredential);
    return(SEC_E_OK);

}

//+-------------------------------------------------------------------------
//
//  Function:   FreeCredentialsHandle
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
FreeCredentialsHandle(
    PCredHandle                 phCredential        // Handle to free
    )
{
    if (DeleteCredential(phCredential->dwUpper))
    {
        return(SEC_E_OK);
    }
    else
    {
        return(SEC_E_UNKNOWN_CREDENTIALS);
    }
}

//+-------------------------------------------------------------------------
//
//  Function:   InitializeSecurityContextW
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
InitializeSecurityContextW(
    PCredHandle                 phCredential,       // Cred to base context
    PCtxtHandle                 phContext,          // Existing context (OPT)
    LPWSTR                      pszTargetName,      // Name of target
    unsigned long               fContextReq,        // Context Requirements
    unsigned long               Reserved1,          // Reserved, MBZ
    unsigned long               TargetDataRep,      // Data rep of target
    PSecBufferDesc              pInput,             // Input Buffers
    unsigned long               Reserved2,          // Reserved, MBZ
    PCtxtHandle                 phNewContext,       // (out) New Context handle
    PSecBufferDesc              pOutput,            // (inout) Output Buffers
    unsigned long SEC_FAR *     pfContextAttr,      // (out) Context attrs
    PTimeStamp                  ptsExpiry           // (out) Life span (OPT)
    )
{
    PCREDENTIAL Credential = NULL;
    PSEC_CONTEXT Context = NULL;
    PMESSAGE Message = NULL;
    PSecBuffer OutputBuffer;
    PSecBuffer InputBuffer;
    MESSAGE SampleMessage;

    if (fContextReq & ISC_REQ_ALLOCATE_MEMORY)
    {
        return(SEC_E_INSUFFICIENT_MEMORY);
    }

    OutputBuffer = LocateSecBuffer(pOutput);
    if (OutputBuffer == NULL)
    {
        return(SEC_E_INVALID_TOKEN);
    }

    if (phContext == NULL)
    {

        Credential = LocateCredential(phCredential->dwUpper);
        if (Credential == NULL)
        {
            return(SEC_E_UNKNOWN_CREDENTIALS);
        }

        if ((Credential->Use & SECPKG_CRED_OUTBOUND) == 0)
        {
            return(SEC_E_UNKNOWN_CREDENTIALS);
        }

    }

    //
    // If the context is NULL, create a new one.
    //

    if (phContext == NULL)
    {
        Context = (PSEC_CONTEXT) LocalAlloc(0,sizeof(SEC_CONTEXT));
        if (Context == NULL)
        {
            return(SEC_E_INSUFFICIENT_MEMORY);
        }
        Context->ContextId = GetNewId();
        phNewContext->dwUpper = Context->ContextId;
        Context->CredentialId = phCredential->dwUpper;
        Context->State = FirstInit;
        Context->Nonce = 0;
        Context->ContextFlags = fContextReq;
        *pfContextAttr = fContextReq;
        *ptsExpiry = Forever;

        //
        // Build an output token.
        //

        Message = (PMESSAGE) OutputBuffer->pvBuffer;
        Message->MessageType = Negotiate;
        memset(Message->Buffer,'x',MESSAGE_SIZE);
        OutputBuffer->cbBuffer = sizeof(MESSAGE);

        AddContext(Context);

        return(SEC_I_CONTINUE_NEEDED);

    } else {
        //
        // This is the second call. Lookup the old context.
        //

        Context = LocateContext(phContext->dwUpper);
        if (Context == NULL)
        {
            return(SEC_E_INVALID_HANDLE);
        }

        if ((Context->State != FirstInit) &&
            (Context->State != SecondInit))
        {
            return(SEC_E_INVALID_HANDLE);
        }

        //
        // Build the new output message
        //

        if (Context->State == FirstInit)
        {

            //
            // Check that the input message is what we expected.
            //

            InputBuffer = LocateSecBuffer(pInput);
            if (InputBuffer == NULL)
            {
                return(SEC_E_INVALID_TOKEN);
            }
            Message = (PMESSAGE) InputBuffer->pvBuffer;

            SampleMessage.MessageType = Challenge;
            memset(SampleMessage.Buffer, 'y', MESSAGE_SIZE);

            if (memcmp(&SampleMessage,Message,MESSAGE_SIZE) != 0)
            {
                return(SEC_E_INVALID_TOKEN);
            }

            Message = (PMESSAGE) OutputBuffer->pvBuffer;
            Message->MessageType = ChallengeResponse;
            memset(Message->Buffer,'z',MESSAGE_SIZE);
        }
        else
        {
            Message = (PMESSAGE) OutputBuffer->pvBuffer;
            Message->MessageType = ReAuthenticate;
            memset(Message->Buffer,'q',MESSAGE_SIZE);
        }

        OutputBuffer->cbBuffer = sizeof(MESSAGE);
        Context->State = SecondInit;
        return(SEC_E_OK);
    }
}

//+------------------------------------------- ------------------------------
//
//  Function:   AcceptSecurityContext
//
//  Synopsis:Allows a remotely initiated security context between the
//              application and a remote peer to be established.  To complete
//              the establishment of context one or more reply tokens may be
//              required from remote peer.
//              This function is the server counterpart to the
//              InitializeSecurityContext API.  The ContextAttributes is a bit
//              mask representing various context level functions such as
//              delegation, mutual authentication, confidentiality, replay
//              detection and sequence detection.  This API is used by the
//              server side.  When a request comes in, the server uses the
//              ContextReqFlags parameter to specify what it requires of the
//              session.  In this fashion, a server can specify that clients
//              must be capable of using a confidential or integrity checked
//              session, and fail clients that can't meet that demand.
//              Alternatively, a server can require nothing, and whatever the
//              client can provide or requires is returned in the
//              pfContextAttributes parameter.  For a package that supports 3
//              leg mutual authentication, the calling sequence would be:
//              Client provides a token, server calls Accept the first time,
//              generating a reply token.  The client uses this in a second
//              call to InitializeSecurityContext, and generates a final token.
//              This token is then used in the final call to Accept to complete
//              the session.  Another example would be the LAN Manager/NT
//              authentication style.  The client connects to negotiate a
//              protocol.  The server calls Accept to set up a context and
//              generate a challenge to the client.  The client calls
//              InitializeSecurityContext and creates a response.  The server
//              then calls Accept the final time to allow the package to verify
//              the response is appropriate for the challenge.
//
//  Effects:
//
//
//  Arguments:[phCredential] - Handle to the credentials to be used to
//              create the context.
//
//              [phContext] - Handle to the partially formed context, if
//              this is a second call (see above) or NULL if this is the first call.
//
//              [pInput] - Pointer to the input token.  In the first call
//              this token can either be NULL or may contain
//              security package specific information.
//
//              [fContextReq] - Requirements of the context, package specific. See
//              the ASC_REQ_xxx defines in SSPI.H.
//
//              [TargetDataRep] - Long indicating the data representation (byte
//              ordering, etc) on the target.  The constant
//              SECURITY_NATIVE_DREP may be supplied by the
//              transport indicating that the native format
//              is in use.
//
//              [phNewContext] - New context handle.  If this is a second
//              call, this can be same as OldContextHandle.
//
//              [pOutput] - Buffer to receive the output token.
//
//              [pfContextAttr] - Attributes of the context established. See
//              the ASC_RET_xxx defines in SSPI.H
//
//              [ptsExpiry] - Expiration time of the context.
//
//  Requires:
//
//  Returns:STATUS_SUCCESS - Message handled
//              SEC_I_CALLBACK_NEEDED - Caller should call again later
//              SEC_E_NO_SPM - Security Support Provider is not running
//              SEC_E_INVALID_TOKEN - Token improperly formatted
//              SEC_E_INVALID_HANDLE - Credential/Context Handle is invalid
//              SEC_E_BUFFER_TOO_SMALL- Buffer for output token isn't big enough
//              SEC_E_LOGON_DENIED - User is no allowed to logon to this server
//              SEC_E_INSUFFICIENT_MEMORY - Not enough memory
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
AcceptSecurityContext(
    PCredHandle                 phCredential,       // Cred to base context
    PCtxtHandle                 phContext,          // Existing context (OPT)
    PSecBufferDesc              pInput,             // Input buffer
    unsigned long               fContextReq,        // Context Requirements
    unsigned long               TargetDataRep,      // Target Data Rep
    PCtxtHandle                 phNewContext,       // (out) New context handle
    PSecBufferDesc              pOutput,            // (inout) Output buffers
    unsigned long SEC_FAR *     pfContextAttr,      // (out) Context attributes
    PTimeStamp                  ptsExpiry           // (out) Life span (OPT)
    )
{
    PCREDENTIAL Credential = NULL;
    PSEC_CONTEXT Context = NULL;
    PMESSAGE Message = NULL;
    PSecBuffer OutputBuffer;
    PSecBuffer InputBuffer;
    MESSAGE SampleMessage;

    if (fContextReq & ISC_REQ_ALLOCATE_MEMORY)
    {
        return(SEC_E_INSUFFICIENT_MEMORY);
    }

    InputBuffer = LocateSecBuffer(pInput);
    if (InputBuffer == NULL)
    {
        return(SEC_E_INVALID_TOKEN);
    }

    if (phContext == NULL)
    {

        Credential = LocateCredential(phCredential->dwUpper);
        if (Credential == NULL)
        {
            return(SEC_E_UNKNOWN_CREDENTIALS);
        }

        if ((Credential->Use & SECPKG_CRED_INBOUND) == 0)
        {
            return(SEC_E_UNKNOWN_CREDENTIALS);
        }

    }

    //
    // If the context is NULL, create a new one.
    //

    if (phContext == NULL)
    {
        //
        // Make sure the output buffer exists.
        //

        OutputBuffer = LocateSecBuffer(pOutput);
        if (OutputBuffer == NULL)
        {
            return(SEC_E_INVALID_TOKEN);
        }

        //
        // Check that the input message is what we expected.
        //

        Message = (PMESSAGE) InputBuffer->pvBuffer;
        SampleMessage.MessageType = Negotiate;
        memset(SampleMessage.Buffer, 'x', MESSAGE_SIZE);

        if (memcmp(&SampleMessage,Message,MESSAGE_SIZE) != 0)
        {
            return(SEC_E_INVALID_TOKEN);
        }

        //
        // Build a new context.
        //

        Context = (PSEC_CONTEXT) LocalAlloc(0,sizeof(SEC_CONTEXT));
        if (Context == NULL)
        {
            return(SEC_E_INSUFFICIENT_MEMORY);
        }
        Context->ContextId = GetNewId();
        phNewContext->dwUpper = Context->ContextId;
        Context->CredentialId = phCredential->dwUpper;
        Context->State = FirstAccept;
        Context->Nonce = 0;
        Context->ContextFlags = fContextReq;
        *pfContextAttr = fContextReq;
        *ptsExpiry = Forever;

        //
        // Build an output token.
        //

        Message = (PMESSAGE) OutputBuffer->pvBuffer;
        Message->MessageType = Challenge;
        memset(Message->Buffer,'y',MESSAGE_SIZE);
        OutputBuffer->cbBuffer = sizeof(MESSAGE);

        AddContext(Context);

        return(SEC_I_CONTINUE_NEEDED);

    } else {
        //
        // This is the second call. Lookup the old context.
        //

        Context = LocateContext(phContext->dwUpper);
        if (Context == NULL)
        {
            return(SEC_E_INVALID_HANDLE);
        }
        if ((Context->State != FirstAccept) &&
            (Context->State != SecondAccept))
        {
            return(SEC_E_INVALID_HANDLE);
        }

        Message = (PMESSAGE) InputBuffer->pvBuffer;

        //
        // Check that the input message is what we expected.
        //

        if (Context->State == FirstAccept)
        {
            SampleMessage.MessageType = ChallengeResponse;
            memset(SampleMessage.Buffer, 'z', MESSAGE_SIZE);
        }
        else
        {
            SampleMessage.MessageType = ReAuthenticate;
            memset(SampleMessage.Buffer, 'q', MESSAGE_SIZE);
        }

        if (memcmp(&SampleMessage,Message,MESSAGE_SIZE) != 0)
        {
            return(SEC_E_INVALID_TOKEN);
        }

        Context->State = SecondAccept;
        return(SEC_E_OK);
    }
}

//+-------------------------------------------------------------------------
//
//  Function:   DeleteSecurityContext
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
DeleteSecurityContext(
    PCtxtHandle                 phContext           // Context to delete
    )
{
    if (DeleteContext(phContext->dwUpper))
    {
        return(SEC_E_OK);
    }
    else
    {
        return(SEC_E_INVALID_HANDLE);
    }
}

//+-------------------------------------------------------------------------
//
//  Function:   ApplyControlToken
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
ApplyControlToken(
    PCtxtHandle                 phContext,          // Context to modify
    PSecBufferDesc              pInput              // Input token to apply
    )
{
    return(SEC_E_UNSUPPORTED_FUNCTION);

}

//+-------------------------------------------------------------------------
//
//  Function:   EnumerateSecurityPackagesW
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
EnumerateSecurityPackagesW(
    unsigned long SEC_FAR *     pcPackages,         // Receives num. packages
    PSecPkgInfoW SEC_FAR *      ppPackageInfo       // Receives array of info
    )
{
    SECURITY_STATUS SecStatus;

    SecStatus = QuerySecurityPackageInfoW(
                    PACKAGE_NAME,
                    ppPackageInfo
                    );
    if (SecStatus == SEC_E_OK)
    {
        *pcPackages = 1;

    }

    return(SecStatus);

}

//+-------------------------------------------------------------------------
//
//  Function:   QuerySecurityPackageInfoW
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
QuerySecurityPackageInfoW(
    LPWSTR                      pszPackageName,     // Name of package
    PSecPkgInfoW SEC_FAR *      ppPackageInfo        // Receives package info
    )
{
    PSecPkgInfoW PackageInfo;
    ULONG PackageInfoSize;
    PUCHAR Where;

    if (_wcsicmp(pszPackageName, PACKAGE_NAME))
    {
        return(SEC_E_SECPKG_NOT_FOUND);
    }

    PackageInfoSize = sizeof(SecPkgInfoW) +
                        (wcslen(PACKAGE_NAME) + 1 +
                         wcslen(PACKAGE_COMMENT) + 1) * sizeof(WCHAR);

    PackageInfo = (PSecPkgInfoW) LocalAlloc(0,PackageInfoSize);
    if (PackageInfo == NULL)
    {
        return(SEC_E_INSUFFICIENT_MEMORY);
    }
    PackageInfo->fCapabilities = PACKAGE_CAPABILITIES;
    PackageInfo->wVersion = PACKAGE_VERSION;
    PackageInfo->wRPCID = PACKAGE_RPCID;
    PackageInfo->cbMaxToken = PACKAGE_MAXTOKEN;

    Where = (PUCHAR) (PackageInfo + 1);
    PackageInfo->Name = (LPWSTR) Where;
    Where += (wcslen(PACKAGE_NAME) + 1) * sizeof(WCHAR);
    wcscpy(PackageInfo->Name, PACKAGE_NAME);

    PackageInfo->Comment = (LPWSTR) Where;
    Where += (wcslen(PACKAGE_COMMENT) + 1) * sizeof(WCHAR);
    wcscpy(PackageInfo->Comment, PACKAGE_COMMENT);

    assert(Where - (PBYTE) PackageInfo == (LONG) PackageInfoSize);

    *ppPackageInfo = PackageInfo;
    return(SEC_E_OK);
}

//+-------------------------------------------------------------------------
//
//  Function:   FreeContextBuffer
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
FreeContextBuffer(
    void SEC_FAR *      pvContextBuffer
    )
{
    LocalFree(pvContextBuffer);
    return(SEC_E_OK);
}

//+-------------------------------------------------------------------------
//
//  Function:   CompleteAuthToken
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
CompleteAuthToken(
    PCtxtHandle                 phContext,          // Context to complete
    PSecBufferDesc              pToken              // Token to complete
    )
{

    return(SEC_E_OK);

}

//+-------------------------------------------------------------------------
//
//  Function:   ImpersonateSecurityContext
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
ImpersonateSecurityContext(
    PCtxtHandle                 phContext           // Context to impersonate
    )
{
    return(SEC_E_OK);
}

//+-------------------------------------------------------------------------
//
//  Function:   RevertSecurityContext
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//

//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
RevertSecurityContext(
    PCtxtHandle                 phContext           // Context from which to re
    )
{
    return(SEC_E_OK);
}

//+-------------------------------------------------------------------------
//
//  Function:   QueryContextAttributes
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
QueryContextAttributesW(
    PCtxtHandle                 phContext,          // Context to query
    unsigned long               ulAttribute,        // Attribute to query
    void SEC_FAR *              pBuffer             // Buffer for attributes
    )
{
    PSEC_CONTEXT Context;
    PSecPkgContext_Sizes ContextSizes;
    PSecPkgContext_NamesW ContextNames;
    PSecPkgContext_Lifespan ContextLifespan;
    PSecPkgContext_DceInfo  ContextDceInfo;

    Context = LocateContext(phContext->dwUpper);

    if (Context == NULL)
    {
        return(SEC_E_INVALID_HANDLE);
    }

    switch(ulAttribute) {
    case SECPKG_ATTR_SIZES:
        ContextSizes = (PSecPkgContext_Sizes) pBuffer;
        ContextSizes->cbMaxSignature = PACKAGE_SIGNATURE_SIZE;
        if ((Context->ContextFlags & ISC_REQ_CONFIDENTIALITY) != 0)
        {
            ContextSizes->cbSecurityTrailer = PACKAGE_SIGNATURE_SIZE;
            ContextSizes->cbBlockSize = 1;
        }
        else
        {
            ContextSizes->cbSecurityTrailer = 0;
            ContextSizes->cbBlockSize = 0;
        }
        ContextSizes->cbMaxToken = PACKAGE_MAXTOKEN;
        break;
    case SECPKG_ATTR_NAMES:
        ContextNames = (PSecPkgContext_Names) pBuffer;
        ContextNames->sUserName = (LPWSTR) LocalAlloc(0,sizeof(L"dummy user"));
        if (ContextNames->sUserName == NULL)
        {
            return(SEC_E_INSUFFICIENT_MEMORY);
        }
        wcscpy(ContextNames->sUserName, L"dummy user");
        break;
    case SECPKG_ATTR_LIFESPAN:
        ContextLifespan = (PSecPkgContext_Lifespan) pBuffer;
        ContextLifespan->tsStart = Never;
        ContextLifespan->tsExpiry = Forever;
        break;
    case SECPKG_ATTR_DCE_INFO:
        ContextDceInfo = (PSecPkgContext_DceInfo) pBuffer;
        ContextDceInfo->AuthzSvc = 0;
        ContextDceInfo->pPac = (PVOID) LocalAlloc(0,sizeof(L"dummy user"));
        if (ContextDceInfo->pPac == NULL)
        {
            return(SEC_E_INSUFFICIENT_MEMORY);
        }
        wcscpy((LPWSTR) ContextDceInfo->pPac, L"dummy user");

        break;
    default:
        return(SEC_E_INVALID_TOKEN);
    }

    return(SEC_E_OK);
}

#if 0

//+-------------------------------------------------------------------------
//
//  Function:   QueryCredentialsAttributes
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

SECURITY_STATUS SEC_ENTRY
QueryCredentialsAttributesW(
    PCredHandle                 phCredentials,      // Credentials to query
    unsigned long               ulAttribute,        // Attribute to query
    void SEC_FAR *              pBuffer             // Buffer for attributes
    )
{
    return( pspPackages[phCredentials->dwLower].pftTableW->QueryCredentialsAttributesW(
                                        phCredentials,
                                        ulAttribute,
                                        pBuffer ) );
}

#endif

//+-------------------------------------------------------------------------
//
//  Function:   MakeSignature
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:  [phContext]     -- context to use
//              [fQOP]          -- quality of protection to use
//              [pMessage]      -- message
//              [MessageSeqNo]  -- sequence number of message
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY
MakeSignature(  PCtxtHandle         phContext,
                ULONG               fQOP,
                PSecBufferDesc      pMessage,
                ULONG               MessageSeqNo)
{
    PSEC_CONTEXT Context;
    PSecBuffer SignatureBuffer;
    PSIGNATURE Signature;

    Context = LocateContext(phContext->dwUpper);
    if (Context == NULL)
    {
        return(SEC_E_INVALID_HANDLE);
    }

    SignatureBuffer = LocateSigBuffer(pMessage);
    if (SignatureBuffer == NULL)
    {
        return(SEC_E_INVALID_TOKEN);
    }
    Signature = (PSIGNATURE) SignatureBuffer->pvBuffer;
    Signature->MessageId = Context->Nonce++;
    Signature->Tag = PACKAGE_SIGNATURE_TAG;
    SignatureBuffer->cbBuffer = sizeof(SIGNATURE);
    return(SEC_E_OK);
}

//+-------------------------------------------------------------------------
//
//  Function:   VerifySignature
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:  [phContext]     -- Context performing the unseal
//              [pMessage]      -- Message to verify
//              [MessageSeqNo]  -- Sequence number of this message
//              [pfQOPUsed]     -- quality of protection used
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY
VerifySignature(PCtxtHandle     phContext,
                PSecBufferDesc  pMessage,
                ULONG           MessageSeqNo,
                ULONG *         pfQOP)
{
    PSEC_CONTEXT Context;
    PSecBuffer SignatureBuffer;
    PSIGNATURE Signature;

    Context = LocateContext(phContext->dwUpper);
    if (Context == NULL)
    {
        return(SEC_E_INVALID_HANDLE);
    }

    SignatureBuffer = LocateSigBuffer(pMessage);
    if (SignatureBuffer == NULL)
    {
        return(SEC_E_INVALID_TOKEN);
    }
    Signature = (PSIGNATURE) SignatureBuffer->pvBuffer;

    if (Signature->Tag != PACKAGE_SIGNATURE_TAG)
    {
        return(SEC_E_MESSAGE_ALTERED);
    }

    if (Signature->MessageId != Context->Nonce++)
    {
        Context->State = Idle;
        return(SEC_E_OUT_OF_SEQUENCE);
    }

    return(SEC_E_OK);
}


README.TXT

How to Put a DACL on Floppy Drives

SUMMARY
=======

There is no way to put a DACL on the floppy drives or on
the COM ports with REGEDT32, or using the Control Panel or other
part of the user interface. Also, there is no way to use the
Win32 API to put a DACL on the floppy drives or on the COM ports
that survives reboots.

However, the SD_FLPPY sample does put DACLs on the floppy drives or on
the COM ports that survive logoff and logon; that is, these DACLs
are on the floppy drives or on the COM ports until the next reboot.
This sample is a good starting place for anyone writing code to apply DACLs.

A version of this sample program can be installed as a service, so
that each time the machine boots up the DACLs are automatically re-applied.

This sample is not a supported utility.

MORE INFORMATION
================

There are possibly as many desired user interfaces to this sort of
functionality as there are people thinking about this, so it is
not a purpose of this sample (or the service variation of
it) to present an incredibly cool user interface to how the
DACLs get applied. A very simplistic approach is taken to the
user interface.

To run
------

Type sd_flppy to lock the \\.\A: and \\.\B: devices.

You can optionally put SD_FLPPY.EXE in the Startup group or a logon script.

Notes
-----

The version of this program that is packaged as a service is
in this same directory and is built along with SD_FLPPY.EXE by
the same makefile.

The packaged-as-a-service approach might better suit people who
need to change the DACL on the floppy drives without requiring a
reboot or logoff. After installing the FLOPLOCK service on
the machine, the client application, CHGFLPSD can be used over the
network to lock, unlock or query the floppy-locked-state of any
machine where the FLOPLOCK service is running.

So, this packaged-as-a-service approach might better suit people
that would like to be able to inquire over the net what the
DACLs are on the floppy drives of particular machines (to check
or audit them). This approach might better suit people that
would prefer that the DACLs be applied as the system boots up so
the DACLs are applied before any user has logged on at the keyboard.

The packaged-as-a-service approach is more aligned with the notion
of protecting the floppies as a resource on a particular
_machine_ (regardless of who if anyone is logged on), whereas
the SD_FLPPY approach (running an .exe at logon time) is
more along the lines of keeping a particular user from using
the floppies on any machine that that user might use. However,
once the user has logged onto the machine and locked the
floppies with SD_FLPPY the floppies will stay locked until
reboot. Of course a utility could easily be written that could
run in the startup group of a different authorized user to force the
floppies on any machine this user logs onto to be unlocked.

As noted above, the FLOPLOCK service is built by the single
makefile in this directory. FLOPLOCK.EXE is the main element of
the packaged-as-a-service version. FLOPLOCK installs as a service, using
INSTSRV.EXE. INSTSRV.EXE, as well as CHGFLPSD.EXE, is also built by the
single makefile in this directory

Run "instsrv" to get help on the switches to use when running
INSTSRV.EXE to install the service.

FLOPLOCK creates a named pipe which CHGFLPSD opens to pass
over the lock/unlock/query operations to a particular machine
running FLOPLOCK.EXE - the named pipe is at all times protected
by an Admins-only DACL

Run "chgflpsd" to get help on the switches to use when running
CHGFLPSD.EXE.


FLOPLOCK.C

/****************************************************************************\
*  INCLUDES, DEFINES
\****************************************************************************/
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

#define PERR(api) printf("\n%s: Error %d from %s on line %d",  \
    __FILE__, GetLastError(), api, __LINE__);
#define PMSG(msg) printf("\n%s line %d: %s",  \
    __FILE__, __LINE__, msg);

// this event is signalled when the
//  worker thread ends
//
HANDLE                  hServDoneEvent = NULL;
SERVICE_STATUS          ssStatus;       // current status of the service

SERVICE_STATUS_HANDLE   sshStatusHandle;
DWORD                   dwGlobalErr;
DWORD                   TID = 0;
HANDLE                  threadHandle = NULL;
HANDLE                  pipeHandle;

/****************************************************************************\
* FUNCTION PROTOTYPES
\****************************************************************************/

VOID    WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
VOID    WINAPI service_ctrl(DWORD dwCtrlCode);
BOOL    ReportStatusToSCMgr(DWORD dwCurrentState,
                            DWORD dwWin32ExitCode,
                            DWORD dwCheckPoint,
                            DWORD dwWaitHint);
VOID    die(char *reason);
VOID    worker_thread(VOID *notUsed);
VOID    StopSimpleService(LPTSTR lpszMsg);
BOOL    WriteSD_ToA_File(PSECURITY_DESCRIPTOR psdAbsoluteSD, LPTSTR lpszFileName);

/****************************************************************************\
* GLOBAL VARIABLES AND TYPEDEFS
\****************************************************************************/

#define                               SZ_SD_BUF   100
#define                               SZ_SID_BUF   75
#define                               SZ_ACL_BUF  150

UCHAR                ucAbsSDBuf      [SZ_SD_BUF]  = "";
UCHAR                ucEvrSDBuf      [SZ_SD_BUF]  = "";
UCHAR                ucSIDBuf        [SZ_SID_BUF] = "";
UCHAR                ucPwrUsrsSIDBuf [SZ_SID_BUF] = "";
UCHAR                ucACLBuf        [SZ_ACL_BUF] = "";

DWORD                dwSID          = SZ_SID_BUF;
DWORD                dwDACL         = SZ_ACL_BUF;
BOOL                 bFloppiesAreLocked;

PSECURITY_DESCRIPTOR psdAbsoluteSD      = (PSECURITY_DESCRIPTOR)&ucAbsSDBuf;
PSECURITY_DESCRIPTOR psdEveryoneSD      = (PSECURITY_DESCRIPTOR)&ucEvrSDBuf;
PSID                 psidAdministrators = (PSID)&ucSIDBuf;
PSID                 psidPowerUsers     = (PSID)&ucPwrUsrsSIDBuf;
PACL                 pNewDACL           = (PACL)&ucACLBuf;

//  main() --
//      all main does is call StartServiceCtrlDispatcher
//      to register the main service thread.  When the
//      API returns, the service has stopped, so exit.
//
int
main()
{

    SERVICE_TABLE_ENTRY dispatchTable[] = {
        { TEXT("SimpleService"), (LPSERVICE_MAIN_FUNCTION)service_main },
        { NULL, NULL }
    };

  // check if Win32s, if so, display notice and terminate
        if( GetVersion() & 0x80000000 )
        {
          MessageBox( NULL,
             "This application cannot run on Windows 3.1 or Windows 95.\n"
             "This application will now terminate.",
             "SD_FLPPY",
             MB_OK | MB_ICONSTOP | MB_SETFOREGROUND );
          return( 1 );
        }

    #define FILE_TO_REDIRECT_STDOUT_TO "c:\\floplock.out"
//  freopen(FILE_TO_REDIRECT_STDOUT_TO,"w+",stdout);

    if (!StartServiceCtrlDispatcher(dispatchTable)) {
        StopSimpleService("StartServiceCtrlDispatcher failed.");
    }
}

//  service_main() --
//      this function takes care of actually starting the service,
//      informing the service controller at each step along the way.
//      After launching the worker thread, it waits on the event
//      that the worker thread will signal at its termination.
//
VOID WINAPI
service_main(DWORD dwArgc, LPTSTR *lpszArgv)
{
    DWORD                   dwWait;
    SECURITY_ATTRIBUTES     sa;

    // register our service control handler:
    //
    sshStatusHandle = RegisterServiceCtrlHandler(
                                    TEXT("SimpleService"),
                                    service_ctrl);

    if (!sshStatusHandle)
        goto cleanup;

    // SERVICE_STATUS members that don't change in example
    //
    ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ssStatus.dwServiceSpecificExitCode = 0;

    // report the status to Service Control Manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        1,                     // checkpoint
        3000))                 // wait hint
        goto cleanup;

    // create the event object. The control handler function signals
    // this event when it receives the "stop" control code.
    //
    hServDoneEvent = CreateEvent(
        NULL,    // no security attributes
        TRUE,    // manual reset event
        FALSE,   // not-signalled
        NULL);   // no name

    if (hServDoneEvent == (HANDLE)NULL)
        goto cleanup;

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        2,                     // checkpoint
        3000))                 // wait hint
        goto cleanup;

    // Create a security descriptor that allows only local Administrators
    //   to do anything with the pipe.  Since Domain administrators are
    //   normally also local Administrators, this will serve most needs

    /************************************************************************\
    *
    * Build SIDs of local Administrators and Power Users.  Note that the Power
    *   Users group is defined on Windows NT machines, but not on Advanced
    *   Server machines.  This means that we will on Advanced Server machines
    *   have a DACL that has an ACE (for Power Users) that will never be used
    *
    \************************************************************************/

    {
      SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;

      InitializeSid(        psidAdministrators, &siaNtAuthority, 2 );
      InitializeSid(        psidPowerUsers,     &siaNtAuthority, 2 );

      *(GetSidSubAuthority( psidAdministrators, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
      *(GetSidSubAuthority( psidPowerUsers,     0 )) = SECURITY_BUILTIN_DOMAIN_RID;

      *(GetSidSubAuthority( psidAdministrators, 1 )) = DOMAIN_ALIAS_RID_ADMINS;
      *(GetSidSubAuthority( psidPowerUsers,     1 )) = DOMAIN_ALIAS_RID_POWER_USERS;
    }

    /************************************************************************\
    *
    * Initialize new DACL
    *
    \************************************************************************/

    if (!InitializeAcl(pNewDACL,
                       dwDACL,
                       ACL_REVISION))
    { StopSimpleService("InitializeAcl");
    }

    /************************************************************************\
    *
    * Allow All access for local Administrators only
    *
    \************************************************************************/

    if (!AddAccessAllowedAce(pNewDACL,
                             ACL_REVISION,
                             FILE_ALL_ACCESS,
                             psidAdministrators))
    { StopSimpleService("AddAccessAllowedAce");
    }

    /************************************************************************\
    *
    * If we unlock the floppies when the service stops, then for the sake of
    *   consistency, we have to also allow Power Users on the Admin-only DACL,
    *   since Power Users can stop services.  It would be inconsistent to try
    *   to lock Power Users away from their floppies if Power Users could get
    *   to the floppies simply by stopping the service
    *
    * It's still OK to use the same DACL for the pipe as for the floppies,
    *   that is, it's OK to let Power Users on the DACL for the pipe too.  The
    *   reason is that it is not generally (and certainly not by default) the
    *   case that an account is a member of Power Users on more than their own
    *   machines.  So, putting Power Users on the pipe let's Power Users admin
    *   the floppies via the pipe only on the machines on which they are
    *   actually Power Users, and again, on those machines they can stop the
    *   floppy-locking service as well
    *
    \************************************************************************/

    #define UNLOCK_AT_SERVICE_STOP (0==0)
    if     (UNLOCK_AT_SERVICE_STOP)
    { if (!AddAccessAllowedAce(pNewDACL,
                               ACL_REVISION,
                               FILE_ALL_ACCESS,
                               psidPowerUsers))
      { StopSimpleService("AddAccessAllowedAce");
      }
    }

    /************************************************************************\
    *
    * Build SD in absolute format - first the Admins-only then the Everyone SD
    *
    \************************************************************************/

    if (!InitializeSecurityDescriptor(psdAbsoluteSD,
                                      SECURITY_DESCRIPTOR_REVISION))
    { StopSimpleService("InitializeSecurityDescriptor");
    }

    if (!InitializeSecurityDescriptor(psdEveryoneSD,
                                      SECURITY_DESCRIPTOR_REVISION))
    { StopSimpleService("InitializeSecurityDescriptor");
    }

    /************************************************************************\
    *
    * Set DACL into SD - first the Admins-only then the Everyone SD
    *
    \************************************************************************/

    if (!SetSecurityDescriptorDacl(psdAbsoluteSD,
                                   TRUE,      // fDaclPresent flag
                                   pNewDACL,
                                   FALSE))    // not a default DACL
    { StopSimpleService("SetSecurityDescriptorDacl");
    }

    if (!SetSecurityDescriptorDacl(psdEveryoneSD,
                                   TRUE,      // fDaclPresent flag
                                   (PACL)NULL,
                                   FALSE))    // not a default DACL
    { StopSimpleService("SetSecurityDescriptorDacl");
    }

    /************************************************************************\
    *
    * Check to see that SD is valid before attempting to write it to the file
    *
    \************************************************************************/

    if (!IsValidSecurityDescriptor(psdAbsoluteSD))
    { StopSimpleService("IsValidSecurityDescriptor");
    }

    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = psdAbsoluteSD;
    sa.bInheritHandle = TRUE;  // why not... we spawn no processes

    // open our named pipe...
    //
    pipeHandle = CreateNamedPipe(
                    "\\\\.\\pipe\\sd_flppy",  // name of pipe
                    PIPE_ACCESS_DUPLEX,     // pipe open mode
                    PIPE_TYPE_MESSAGE |
                    PIPE_READMODE_MESSAGE |
                    PIPE_WAIT,              // pipe IO type
                    1,                      // number of instances
                    0,                      // size of outbuf (0 == allocate as necessary)
                    0,                      // size of inbuf
                    1000,                   // default time-out value
                    &sa);                   // security attributes

    if (!pipeHandle) {
        StopSimpleService("CreateNamedPipe");
        return;
    }

    // Set the same DACL onto the floppies
    //

    /************************************************************************\
    *
    * Write SD to file system - first for A: then B:
    *
    \************************************************************************/

    if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\A:"))
    { StopSimpleService("Write of DACL to A: failed");
    }

    if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\B:"))
    { StopSimpleService("Write of DACL to B: failed");
    }

    bFloppiesAreLocked = TRUE;

    /************************************************************************\
    *
    * only works for CDROM drives if you revoke the SeChangeNotify privilege
    * from Everyone in user manager
    *
    \************************************************************************/
  /*
    if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\E:"))
    { StopSimpleService("Write of DACL to E: failed");
    }
  */
    /************************************************************************\
    *
    * Works for COM ports as well - commented out as this samples is floppy only
    *
    \************************************************************************/
  /*
    if (!WriteSD_ToA_File(psdAbsoluteSD,"COM1:"))
    { StopSimpleService("Write of DACL to COM1: failed");
    }
  */

    // start the thread that performs the work of the service.
    //
    threadHandle = CreateThread(
                    NULL,       // security attributes
                    0,          // stack size (0 means inherit parent's stack size)
                    (LPTHREAD_START_ROUTINE)worker_thread,
                    NULL,       // argument to thread
                    0,          // thread creation flags
                    &TID);      // pointer to thread ID

    if (!threadHandle)
        goto cleanup;

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_RUNNING, // service state
        NO_ERROR,        // exit code
        0,               // checkpoint
        0))              // wait hint
        goto cleanup;

    // wait indefinitely until hServDoneEvent is signaled.
    //
    dwWait = WaitForSingleObject(
        hServDoneEvent,  // event object
        INFINITE);       // wait indefinitely

cleanup:

    if (hServDoneEvent != NULL)
        CloseHandle(hServDoneEvent);

    // try to report the stopped status to the service control manager.
    //
    if (sshStatusHandle != 0)
        (VOID)ReportStatusToSCMgr(
                            SERVICE_STOPPED,
                            dwGlobalErr,
                            0,
                            0);

    // When SERVICE MAIN FUNCTION returns in a single service
    // process, the StartServiceCtrlDispatcher function in
    // the main thread returns, terminating the process.
    //
    return;
}

//  service_ctrl() --
//      this function is called by the Service Controller whenever
//      someone calls ControlService in reference to our service.
//
VOID WINAPI
service_ctrl(DWORD dwCtrlCode)
{
    DWORD  dwState = SERVICE_RUNNING;

    // Handle the requested control code.
    //
    switch(dwCtrlCode) {

        // Pause the service if it is running.
        //
        case SERVICE_CONTROL_PAUSE:

            if (ssStatus.dwCurrentState == SERVICE_RUNNING) {
                SuspendThread(threadHandle);
                dwState = SERVICE_PAUSED;
            }
            break;

        // Resume the paused service.
        //
        case SERVICE_CONTROL_CONTINUE:

            if (ssStatus.dwCurrentState == SERVICE_PAUSED) {
                ResumeThread(threadHandle);
                dwState = SERVICE_RUNNING;
            }
            break;

        // Stop the service.
        //
        case SERVICE_CONTROL_STOP:

            dwState = SERVICE_STOP_PENDING;

            // Report the status, specifying the checkpoint and waithint,
            //  before setting the termination event.
            //
            ReportStatusToSCMgr(
                    SERVICE_STOP_PENDING, // current state
                    NO_ERROR,             // exit code
                    1,                    // checkpoint
                    3000);                // waithint

            if     (UNLOCK_AT_SERVICE_STOP)
            { if (!WriteSD_ToA_File(psdEveryoneSD,"\\\\.\\A:"))
              { StopSimpleService("Unlock of A: failed, see log file");
              }
              if (!WriteSD_ToA_File(psdEveryoneSD,"\\\\.\\B:"))
              { StopSimpleService("Unlock of B: failed, see log file");
              }

              bFloppiesAreLocked = FALSE;
            }

            SetEvent(hServDoneEvent);
            return;

        // Update the service status.
        //
        case SERVICE_CONTROL_INTERROGATE:
            break;

        // invalid control code
        //
        default:
            break;

    }

    // send a status response.
    //
    ReportStatusToSCMgr(dwState, NO_ERROR, 0, 0);
}

//  worker_thread() --
//      this function does the actual nuts and bolts work that
//      the service requires.  It will also Pause or Stop when
//      asked by the service_ctrl function.
//
VOID
worker_thread(VOID *notUsed)
{
    char                 inbuf[180];
    char                 outbuf[180];
    BOOL                 ret;
    DWORD                bytesRead;
    DWORD                bytesWritten;
    DWORD                dwLen;

    // okay, our pipe has been created, let's enter the simple
    //  processing loop...
    //
    while (1) {

        // wait for a connection...
        //
        ConnectNamedPipe(pipeHandle, NULL);

        // grab whatever's coming through the pipe...
        //
        ret = ReadFile(
                    pipeHandle,     // file to read from
                    inbuf,          // address of input buffer
                    sizeof(inbuf),  // number of bytes to read
                    &bytesRead,     // number of bytes read
                    NULL);          // overlapped stuff, not needed

        if (!ret)
            // pipe's broken... go back and reconnect
            //
            continue;

        switch (inbuf[0])
        { case 'U':
            dwLen = sprintf(outbuf,"Floppies were unlocked");

            if (!WriteSD_ToA_File(psdEveryoneSD,"\\\\.\\A:"))
            { dwLen += sprintf(outbuf+dwLen,", unlock of A: failed, see log file");
            }
            if (!WriteSD_ToA_File(psdEveryoneSD,"\\\\.\\B:"))
            { dwLen += sprintf(outbuf+dwLen,", unlock of B: failed, see log file");
            }

            bFloppiesAreLocked = FALSE;
            break;

          case 'L':
            dwLen = sprintf(outbuf,"Floppies were locked");

            if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\A:"))
            { dwLen += sprintf(outbuf+dwLen,", lock of A: failed, see log file");
            }
            if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\B:"))
            { dwLen += sprintf(outbuf+dwLen,", lock of B: failed, see log file");
            }

            bFloppiesAreLocked = TRUE;
            break;

          case 'Q':
            if (bFloppiesAreLocked)
            { sprintf(outbuf,"Floppy status is: Locked");
            }
            else
            { sprintf(outbuf,"Floppy status is: Unlocked");
            }
            break;

          default :
            sprintf(outbuf,"Bad operation passed in");
        }

        // send it back out...
        //
        ret = WriteFile(
                    pipeHandle,     // file to write to
                    outbuf,         // address of output buffer
                    sizeof(outbuf), // number of bytes to write
                    &bytesWritten,  // number of bytes written
                    NULL);          // overlapped stuff, not needed

        if (!ret)
            // pipe's broken... go back and reconnect
            //
            continue;

        // drop the connection...
        //
        DisconnectNamedPipe(pipeHandle);
    }
}

// utility functions...

// ReportStatusToSCMgr() --
//      This function is called by the ServMainFunc() and
//      ServCtrlHandler() functions to update the service's status
//      to the service control manager.
//
BOOL
ReportStatusToSCMgr(DWORD dwCurrentState,
                    DWORD dwWin32ExitCode,
                    DWORD dwCheckPoint,
                    DWORD dwWaitHint)
{
    BOOL fResult;

    // Disable control requests until the service is started.
    //
    if (dwCurrentState == SERVICE_START_PENDING)
        ssStatus.dwControlsAccepted = 0;
    else
        ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
            SERVICE_ACCEPT_PAUSE_CONTINUE;

    // These SERVICE_STATUS members are set from parameters.
    //
    ssStatus.dwCurrentState = dwCurrentState;
    ssStatus.dwWin32ExitCode = dwWin32ExitCode;
    ssStatus.dwCheckPoint = dwCheckPoint;

    ssStatus.dwWaitHint = dwWaitHint;

    // Report the status of the service to the service control manager.
    //
    if (!(fResult = SetServiceStatus(
                sshStatusHandle,    // service reference handle
                &ssStatus))) {      // SERVICE_STATUS structure

        // If an error occurs, stop the service.
        //
        StopSimpleService("SetServiceStatus");
    }
    return fResult;
}

// The StopSimpleService function can be used by any thread to report an
//  error, or stop the service.
//
VOID
StopSimpleService(LPTSTR lpszMsg)
{
    CHAR    chMsg[256];
    HANDLE  hEventSource;
    LPTSTR  lpszStrings[2];

    dwGlobalErr = GetLastError();

    // Use event logging to log the error.
    //
    hEventSource = RegisterEventSource(NULL,
                            TEXT("SimpleService"));

    sprintf(chMsg, "SimpleService error: %d", dwGlobalErr);
    lpszStrings[0] = chMsg;
    lpszStrings[1] = lpszMsg;

    if (hEventSource != NULL) {
        ReportEvent(hEventSource, // handle of event source
            EVENTLOG_ERROR_TYPE,  // event type
            0,                    // event category
            0,                    // event ID
            NULL,                 // current user's SID
            2,                    // strings in lpszStrings
            0,                    // no bytes of raw data
            lpszStrings,          // array of error strings
            NULL);                // no raw data

        (VOID) DeregisterEventSource(hEventSource);
    }

    // Set a termination event to stop SERVICE MAIN FUNCTION.
    //
    SetEvent(hServDoneEvent);
}

/****************************************************************************\
*
* FUNCTION: WriteSD_ToA_File
*
\****************************************************************************/

BOOL WriteSD_ToA_File(PSECURITY_DESCRIPTOR psdAbsoluteSD, LPTSTR lpszFileName)
{
  DWORD dwErrorMode;
  BOOL  bStatus;

  /**************************************************************************\
  *
  * SetErrorMode so we don't get the error due to no floppy disk in the floppy
  *   drive
  *
  \**************************************************************************/

  dwErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);

  /**************************************************************************\
  *
  * Write SD to file system
  *
  \**************************************************************************/

  bStatus = SetFileSecurity(lpszFileName,
                            (SECURITY_INFORMATION)(DACL_SECURITY_INFORMATION),
                            psdAbsoluteSD);

  /**************************************************************************\
  *
  * SetErrorMode back to its previous value
  *
  \**************************************************************************/

  SetErrorMode(dwErrorMode);

  if (!bStatus)
  { if (ERROR_FILE_NOT_FOUND == GetLastError())
    { printf("\nAttempted to lock %s, but it was not found",lpszFileName);
    }
    else
    { PERR("SetFileSecurity");
      return(FALSE);
    }
  }

  return(TRUE);
}


INSTSRV.C

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FORCE_SERVICE_NAME_TO_FLOPPYLOCK (1==0)

#define PERR(api) printf("\n%s: Error %d from %s on line %d",  \
    __FILE__, GetLastError(), api, __LINE__);
#define PMSG(msg) printf("\n%s line %d: %s",  \
    __FILE__, __LINE__, msg);

#define MSG_FOR_ACCESS_DENIED "You aren't authorized to do this - please see your system Administrator"
#define MSG_1_FOR_BAD_PATH "The fully qualified path name to the .exe must be given, and"
#define MSG_2_FOR_BAD_PATH "  the drive letter must be for a fixed disk (e.g., not a net drive)"

SC_HANDLE schService;
SC_HANDLE schSCManager;

VOID DisplayHelp(VOID);

VOID InstallService(LPCTSTR serviceName, LPCTSTR serviceExe)
{
  LPCTSTR lpszBinaryPathName = serviceExe;
  LPTSTR  lpszRootPathName="?:\\";

  if ( (':' != *(lpszBinaryPathName+1)) || ('\\' != *(lpszBinaryPathName+2)) )
  { printf("\n%s",MSG_1_FOR_BAD_PATH);
    printf("\n%s\n",MSG_2_FOR_BAD_PATH);
    return;
  }

  #define DRIVE_TYPE_INDETERMINATE 0
  #define ROOT_DIR_DOESNT_EXIST    1

  *lpszRootPathName = *(lpszBinaryPathName+0) ;

  switch (  GetDriveType(lpszRootPathName)  )
  {
    case DRIVE_FIXED :
    { // OK
      break;
    }
    case  ROOT_DIR_DOESNT_EXIST :
    { printf("\n%s",MSG_1_FOR_BAD_PATH);
      printf("\n  the root directory where the .exe is specified to be must exist, and");
      printf("\n%s\n",MSG_2_FOR_BAD_PATH);
      return;
    }
    case  DRIVE_TYPE_INDETERMINATE :
    case  DRIVE_REMOVABLE          :
    case  DRIVE_REMOTE             :
    case  DRIVE_CDROM              :
    case  DRIVE_RAMDISK            :
    { printf("\n%s",MSG_1_FOR_BAD_PATH);
      printf("\n%s\n",MSG_2_FOR_BAD_PATH);
      return;
    }
    default :
    { printf("\n%s",MSG_1_FOR_BAD_PATH);
      printf("\n%s\n",MSG_2_FOR_BAD_PATH);
      return;
    }
  }

  if (INVALID_HANDLE_VALUE == CreateFile(lpszBinaryPathName,
                                         GENERIC_READ,
                                         FILE_SHARE_READ,
                                         NULL,
                                         OPEN_EXISTING,
                                         FILE_ATTRIBUTE_NORMAL,
                                         NULL))
  { printf("\n%s",MSG_1_FOR_BAD_PATH);
    printf("\n  the file must exist, and");
    printf("\n%s\n",MSG_2_FOR_BAD_PATH);
    return;
  }

  if (FORCE_SERVICE_NAME_TO_FLOPPYLOCK)
  { schService = CreateService(
        schSCManager,               // SCManager database
        serviceName,                // name of service
        serviceName,                // name to display (new parameter after october beta)
        SERVICE_ALL_ACCESS,         // desired access
        SERVICE_WIN32_OWN_PROCESS,  // service type
        SERVICE_AUTO_START,         // start type
        SERVICE_ERROR_NORMAL,       // error control type
        lpszBinaryPathName,         // service's binary
        NULL,                       // no load ordering group
        NULL,                       // no tag identifier
        NULL,                       // no dependencies
        NULL,                       // Local System account
        NULL);                      // null password
  }
  else
  { schService = CreateService(
        schSCManager,               // SCManager database
        serviceName,                // name of service
        serviceName,                // name to display (new parameter after october beta)
        SERVICE_ALL_ACCESS,         // desired access
        SERVICE_WIN32_OWN_PROCESS,  // service type
        SERVICE_AUTO_START,         // start type
        SERVICE_ERROR_NORMAL,       // error control type
        lpszBinaryPathName,         // service's binary
        NULL,                       // no load ordering group
        NULL,                       // no tag identifier
        NULL,                       // no dependencies
        ".\\Administrator",         // Admin account
        "");                        // likely incorrect password
  }

  if (NULL == schService)
  { switch (GetLastError())
    {
      case ERROR_ACCESS_DENIED :
      { printf("\n%s",MSG_FOR_ACCESS_DENIED);
        break;
      }
      case ERROR_SERVICE_EXISTS :
      { printf("\nThe %s service is already installed",serviceName);
        printf("\nRemove it first if you need to re-install a new version\n");
        break;
      }
      default :
      { PERR("CreateService");
      }
    }
    return;
  }
  else
  { if (FORCE_SERVICE_NAME_TO_FLOPPYLOCK)
    { printf("\nThe FloppyLock service was installed successfully.\n");
      printf("\nImportant! Before you can use the FloppyLock service, start Control");
      printf("\nPanel and choose the Services applet.  Then select the FloppyLock");
      printf("\nservice and press Startup.  Select System Account, or else FloppyLock");
      printf("\nwill not be able to start properly.  If you want the service to start");
      printf("\nautomatically, select Automatic.\n");
    }
    else
    { printf("\nCreateService SUCCESS\n");
      printf("\nDon't forget!!! You must now go to the Control Panel and");
      printf("\n  use the Services applet to change the account name and");
      printf("\n  password that this newly installed service will use when");
      printf("\n  it attempts to logon as a service when it starts.");
      printf("\nTo do this: use the Startup button in the Services applet,");
      printf("\n  and (for example) specify the Administrator account and");
      printf("\n  correct password.");
      printf("\nAlso, use the Services applet to ensure this newly installed");
      printf("\n  service starts automatically, since the point of this");
      printf("\n  service is to start automatically and apply the DACLs");
      printf("\n  to the floppy drives prior to a user logging on at");
      printf("\n  the keyboard\n");
    }
  }

  CloseServiceHandle(schService);
}

VOID RemoveService(LPCTSTR serviceName)
{
  {
    #define                                     SZ_ENUM_BUF 4096
    ENUM_SERVICE_STATUS        essServiceStatus[SZ_ENUM_BUF];
    DWORD   dwBufSize = sizeof(essServiceStatus);
    DWORD   dwBytesNeeded      = 0;
    DWORD   dwServicesReturned = 0;
    DWORD   dwResumeHandle     = 0;
    DWORD   dwI                = 0;
    BOOLEAN bFound = FALSE;

    if (!EnumServicesStatus(schSCManager,
                            SERVICE_WIN32,
                            SERVICE_ACTIVE,
                            (LPENUM_SERVICE_STATUS)&essServiceStatus,
                            dwBufSize,
                            &dwBytesNeeded,
                            &dwServicesReturned,
                            &dwResumeHandle))
    { switch (GetLastError())
      {
        case ERROR_ACCESS_DENIED :
        { printf("\n%s",MSG_FOR_ACCESS_DENIED);
          break;
        }
        default :
        { PERR("EnumServicesStatus");
        }
      }
      return;
    }

    for (dwI=0; dwI<dwServicesReturned; dwI++)
    { if(0 == _stricmp(essServiceStatus[dwI].lpServiceName,serviceName))
      { bFound = TRUE;
        break;
      }
    }

    if (bFound)
    { printf("\nThe %s service cannot be removed until it has been stopped.",serviceName);
      printf("\nTo stop the %s service, use the Stop button in the Control",serviceName);
      printf("\n  Panel Services applet\n");
      return;
    }
  }

  schService = OpenService(schSCManager,
                           serviceName,
                           SERVICE_ALL_ACCESS);
  if (NULL == schService)
  { switch (GetLastError())
    {
      case ERROR_ACCESS_DENIED :
      { printf("\n%s",MSG_FOR_ACCESS_DENIED);
        break;
      }
      case ERROR_SERVICE_DOES_NOT_EXIST :
      { printf("\nThe %s service is not installed, so cannot be removed\n",serviceName);
        break;
      }
      default :
      { PERR("OpenService");
      }
    }
    return;
  }

  if (DeleteService(schService))
  { printf("\nDeleteService SUCCESS\n");
  }
  else
  { switch (GetLastError())
    {
      case ERROR_ACCESS_DENIED :
      { printf("\n%s",MSG_FOR_ACCESS_DENIED);
        break;
      }
      default :
      { PERR("DeleteService");
      }
    }
  }
}

int main(int argc, char *argv[])
{
  #define           SZ_NAME_BUF  270  // 256 is max, add a little
  UCHAR   ucNameBuf[SZ_NAME_BUF] = "FloppyLock";
  LPTSTR  lpszServName = (LPTSTR)&ucNameBuf;

  UCHAR   ucExeNBuf[SZ_NAME_BUF] = "";
  LPTSTR  lpszExeName  = (LPTSTR)&ucExeNBuf;

  BOOL    bRemovingService = FALSE;
  char *p;

  // check if Win32s, if so, display notice and terminate
      if( GetVersion() & 0x80000000 )
      {
        MessageBox( NULL,
           "This application cannot run on Windows 3.1 or Windows 95.\n"
           "This application will now terminate.",
           "SD_FLPPY",
           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND );
        return( 1 );
      }

  if ( (argc != 2) && (argc != 3) )
  { DisplayHelp();
    exit(1);
  }

  p=argv[1];
  if (    ('/' == *p)
       || ('-' == *p) )
  { DisplayHelp();
    exit(1);
  }

  if (FORCE_SERVICE_NAME_TO_FLOPPYLOCK)
  { if (argc != 2)
    { DisplayHelp();
      exit(1);
    }
    else
    { strcpy(lpszExeName ,argv[1]);

      bRemovingService = (!stricmp(argv[1], "remove"));
    }
  }
  else
  { if (argc != 3)
    { DisplayHelp();
      exit(1);
    }
    else
    { if (strlen(argv[1]) > 256)
      { printf("\nThe service name cannot be longer than 256 characters\n");
        exit(1);
      }
      strcpy(lpszServName,argv[1]);
      strcpy(lpszExeName ,argv[2]);

      bRemovingService = (!stricmp(argv[2], "remove"));
    }
  }

  schSCManager = OpenSCManager(
                      NULL,                   // machine (NULL == local)
                      NULL,                   // database (NULL == default)
                      SC_MANAGER_ALL_ACCESS); // access required
  if (NULL == schSCManager)
  { switch (GetLastError())
    {
      case ERROR_ACCESS_DENIED :
      { printf("\n%s",MSG_FOR_ACCESS_DENIED);
        break;
      }
      default :
      { PERR("OpenSCManager");
      }
    }
    return (0);
  }

  if (bRemovingService)
  { RemoveService(lpszServName);
  }
  else
  { InstallService(lpszServName,lpszExeName);
  }

  CloseServiceHandle(schSCManager);
}

VOID DisplayHelp(VOID)
{
  if (FORCE_SERVICE_NAME_TO_FLOPPYLOCK)
  { printf("\nInstalls or removes the FloppyLock service.  When the FloppyLock");
    printf("\nservice is started, only members of the Administrators group can use");
    printf("\nthe floppy drives on the computer.\n");
    printf("\nTo install the FloppyLock service, type INSTSRV <path>\n");
    printf("\n    path    Absolute path to the FloppyLock service, floplock.exe.  You must");
    printf("\n            use a fully qualified path and the drive letter must be for a");
    printf("\n            fixed, local drive.  For example, INSTSRV c:\\reskit\\floplock.exe.\n");
    printf("\nTo remove the FloppyLock service, type INSTSRV remove\n");
  }
  else
  { printf("\nUsage: instsrv <service name> <exe location>");
    printf("\n           to install a service, or:");
    printf("\n       instsrv <service name> remove");
    printf("\n           to remove a service\n");
    printf("\nIn the case of this service, a more specific example");
    printf("\n       instsrv FloppyLocker c:\\q_a\\samples\\sd_flppy\\floplock.exe");
    printf("\n           (note fully-qualified path name to .exe)");
    printf("\n       instsrv FloppyLocker remove\n");
  }
  return;
}


CHGFLPSD.C

////////////////////////////////////////////////////////
//
//  Client.c --
//
//      This program is a command line oriented
//      demonstration of the FloppyLocker service
//      sample, aka floplock.exe
//
//      Copyright 1992 - 1998 Microsoft Corp.  All Rights Reserved

/****************************************************************************\
*  INCLUDES, DEFINES, TYPEDEFS
\****************************************************************************/
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PERR(api) printf("\n%s: Error %d from %s on line %d",  \
    __FILE__, GetLastError(), api, __LINE__);
#define PMSG(msg) printf("\n%s line %d: %s",  \
    __FILE__, __LINE__, msg);

/****************************************************************************\
* GLOBAL VARIABLES
\****************************************************************************/

#define              SZ_NAME_BUF 30
UCHAR   ucMchNameBuf[SZ_NAME_BUF] = "";
LPTSTR  lpszMchName = (LPTSTR)&ucMchNameBuf;
UCHAR   ucOperation;

/****************************************************************************\
* FUNCTION PROTOTYPES
\****************************************************************************/

BOOL CrackArgs(UINT argc, char *argv[]);
VOID DisplayHelp(VOID);

int main(int argc, char *argv[])
{
  char    inbuf[180];
  char    outbuf[180];
  DWORD   bytesRead;
  BOOL    ret;
  #define               SZ_PIPE_NAME_BUF 50
  UCHAR   ucPipeNameBuf[SZ_PIPE_NAME_BUF] = "";
  LPTSTR  lpszPipeName = (LPTSTR)&ucPipeNameBuf;

  // check if Win32s, if so, display notice and terminate
      if( GetVersion() & 0x80000000 )
      {
        MessageBox( NULL,
           "This application cannot run on Windows 3.1 or Windows 95.\n"
           "This application will now terminate.",
           "SD_FLPPY",
           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND );
        return( 1 );
      }

  if (!CrackArgs(argc,argv))
    exit(1);

  strcpy(lpszPipeName,lpszMchName);
  strcat(lpszPipeName,"\\pipe\\sd_flppy");

  outbuf[0] = ucOperation;
  outbuf[1] = '\0';

  ret = CallNamedPipe(lpszPipeName,
                      outbuf,
                      sizeof(outbuf),
                      inbuf,
                      sizeof(inbuf),
                      &bytesRead,
                      NMPWAIT_WAIT_FOREVER);

  if (!ret)
  { if (ERROR_ACCESS_DENIED == GetLastError())
    { printf("\nAccess denied\n");
    }
    else if (ERROR_BAD_NETPATH == GetLastError())
    { printf("\nMachine %s not found\n",lpszMchName);
    }
    else
    { PERR("CallNamedPipe");
    }
    exit(1);
  }

  printf("\n%s %s\n",lpszMchName,inbuf);
}

/****************************************************************************\
*
* FUNCTION: CrackArgs
*
\****************************************************************************/

BOOL CrackArgs(UINT argc, char *argv[])
{
  char *p;

  /**************************************************************************\
  *
  * There must be two arguments
  *
  \**************************************************************************/

  if (argc != 3)
  { DisplayHelp();
    return(FALSE);
  }

  p=argv[1];

  /**************************************************************************\
  *
  * The machine name argument must be 3 to (SZ_NAME_BUF-3) chars long
  *
  \**************************************************************************/

  if ((strlen(p) < 3) || (strlen(p) > (SZ_NAME_BUF-3) ))
  { DisplayHelp();
    return(FALSE);
  }

  /**************************************************************************\
  *
  * The first two chars in the machine name argument must be \
  *
  \**************************************************************************/

  if ('\\' != *p)
  { DisplayHelp();
    return(FALSE);
  }
  if ('\\' != *(p+1))
  { DisplayHelp();
    return(FALSE);
  }

  /**************************************************************************\
  *
  * Set up the machine name
  *
  \**************************************************************************/

  strcpy(lpszMchName,"\\\\");
  strcat(lpszMchName,(p+2));

  p=argv[2];

  /**************************************************************************\
  *
  * The switch argument must be 2 chars long
  *
  \**************************************************************************/

  if (strlen(p) != 2)
  { DisplayHelp();
    return(FALSE);
  }

  /**************************************************************************\
  *
  * The first char in the switch argument must be /
  *
  \**************************************************************************/

  if ('/' != *p)
  { DisplayHelp();
    return(FALSE);
  }

  /**************************************************************************\
  *
  * Chars 2 must be U or L or Q
  *
  \**************************************************************************/

  switch (*(p+1))
  { case 'u':
    case 'U':
      ucOperation = 'U';
      break;
    case 'l':
    case 'L':
      ucOperation = 'L';
      break;
    case 'q':
    case 'Q':
      ucOperation = 'Q';
      break;
    default :
      DisplayHelp();
      return(FALSE);
  }

  return(TRUE);
}

/****************************************************************************\
*
* FUNCTION: DisplayHelp
*
\****************************************************************************/

VOID DisplayHelp(VOID)
{
  printf("\nusage: chgflpsd \\\\machinenam /switch");
  printf("\n                  Values for /switch");
  printf("\n                        /u      Unlock");
  printf("\n                        /l      Lock");
  printf("\n                        /q      Query\n");
  printf("\nFor example to manage DACLs on machine \\\\flip5's floppies");
  printf("\n       chgflpsd \\\\flip5 /u      Gives access to everyone");
  printf("\n       chgflpsd \\\\flip5 /l      Gives access to only local admins");
  printf("\n       chgflpsd \\\\flip5 /q      Query current access on \\\\flip5\n");

  return;
}


SD_FLPPY.C

/****************************************************************************\
*
*               Microsoft Developer Support
*               Copyright 1992 - 1998 Microsoft Corporation
*
* MODULE:       sd_flppy.c
*
*               There is no way to put a DACL on the floppy drives or on the
*                 COM ports with REGEDT32, or using the Control Panel or other
*                 part of the user interface.  And there is no way to use the
*                 Win32 api to put a DACL on the floppy drives or on the COM
*                 ports that survives reboots
*
*               sd_flppy.c, however, does put DACLs on the floppy drives or on
*                 the COM ports that survive logoff/logon, that is, these
*                 DACLs are on the floppy drives or on the COM ports until the
*                 next reboot
*
*               A version of this sample program can be installed as a
*                 service, so that each time the machine boots up the DACLs
*                 are automatically re-applied
*
* PURPOSE:      Show sample code that applies DACLs to floppy drives and COM
*                 ports
*
*               There are possibly as many desired user interfaces to this
*                 sort of functionality as there are people thinking about
*                 this, so it is not a purpose of this sample (or the Win32
*                 service variation of it) to present an incredibly cool user
*                 interface to how the DACLs get applied.  A very simplistic
*                 approach is taken to the user interface.  Anyone who desires
*                 that more complicated DACLs are applied, or desires other
*                 variations in the user interface hopefully will benefit by
*                 being able to use this sample code as a starting point for
*                 their DACL-applying app
*
*               This sample is not a supported utility
*
* TO RUN:       Type sd_flppy to lock the \\.\A: and \\.\B devices
*
*               Putting sd_flppy in a Startup group or logon script could work
*                 for some people
*
\****************************************************************************/

/****************************************************************************\
*  INCLUDES, DEFINES
\****************************************************************************/
#define STRICT
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>

#define PERR(api) printf("\n%s: Error %d from %s on line %d",  \
    __FILE__, GetLastError(), api, __LINE__);
#define PMSG(msg) printf("\n%s line %d: %s",  \
    __FILE__, __LINE__, msg);

/****************************************************************************\
* GLOBAL VARIABLES AND TYPEDEFS
\****************************************************************************/

/****************************************************************************\
* FUNCTION PROTOTYPES
\****************************************************************************/

BOOL WriteSD_ToA_File(PSECURITY_DESCRIPTOR psdAbsoluteSD, LPTSTR lpszFileName);
VOID DisplayHelp(VOID);

UINT main(UINT argc, char *argv[])
{

  #define                               SZ_SD_BUF   100 // DWORD aligned
  #define                               SZ_SID_BUF   80 // DWORD aligned
  #define                               SZ_ACL_BUF  160 // DWORD aligned

  DWORD                ucAbsSDBuf      [SZ_SD_BUF / sizeof(DWORD) ] ;
  DWORD                ucSIDBuf        [SZ_SID_BUF / sizeof(DWORD) ] ;
  DWORD                ucACLBuf        [SZ_ACL_BUF / sizeof(DWORD) ] ;

  DWORD                dwSID          = SZ_SID_BUF;
  DWORD                dwDACL         = SZ_ACL_BUF;

  PSECURITY_DESCRIPTOR psdAbsoluteSD      = (PSECURITY_DESCRIPTOR)&ucAbsSDBuf;
  PSID                 psidAdministrators = (PSID)&ucSIDBuf;
  PACL                 pNewDACL           = (PACL)&ucACLBuf;

  // check if Win32s, if so, display notice and terminate
      if( GetVersion() & 0x80000000 )
      {
        MessageBox( NULL,
           "This application cannot run on Windows 3.1 or Windows 95.\n"
           "This application will now terminate.",
           "SD_FLPPY",
           MB_OK | MB_ICONSTOP | MB_SETFOREGROUND );
        return( 1 );
      }

  /**************************************************************************\
  *
  * Display help if any parameters passed in
  *
  \**************************************************************************/

  if (argc != 1)
  { DisplayHelp();
    return(1);
  }

  /**************************************************************************\
  *
  * Get SID of local Administrators
  *
  \**************************************************************************/

  {
    SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;

    //
    // preprate a Sid representing the well-known admin group
    //
    InitializeSid( psidAdministrators, &siaNtAuthority, 2 );
    *(GetSidSubAuthority( psidAdministrators, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
    *(GetSidSubAuthority( psidAdministrators, 1 )) = DOMAIN_ALIAS_RID_ADMINS;
  }

  /**************************************************************************\
  *
  * Initialize new DACL
  *
  \**************************************************************************/

    //
    // compute size of acl based on the Sids we will add
    //

    dwDACL = sizeof(ACL) +
        1 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
        GetSidLengthRequired( 2 ) ; // well-known Admins Sid

    if(dwDACL > SZ_ACL_BUF) {
        PERR("Acl larger than buffer!");
        return(1);
    }

  if (!InitializeAcl(pNewDACL,
                     dwDACL,
                     ACL_REVISION))
  { PERR("InitializeAcl");
    return(1);
  }

  /**************************************************************************\
  *
  * Allow All access to the floppy for local Administrators only
  *
  \**************************************************************************/

  if (!AddAccessAllowedAce(pNewDACL,
                           ACL_REVISION,
                           FILE_ALL_ACCESS,
                           psidAdministrators))
  { PERR("AddAccessAllowedAce");
    return(1);
  }

  /**************************************************************************\
  *
  * Build SD in absolute format
  *
  \**************************************************************************/

  if (!InitializeSecurityDescriptor(psdAbsoluteSD,
                                    SECURITY_DESCRIPTOR_REVISION))
  { PERR("InitializeSecurityDescriptor");
    return(1);
  }

  /**************************************************************************\
  *
  * Set DACL into SD
  *
  \**************************************************************************/

  if (!SetSecurityDescriptorDacl(psdAbsoluteSD,
                                 TRUE,      // fDaclPresent flag
                                 pNewDACL,
                                 FALSE))    // not a default DACL
  { PERR("SetSecurityDescriptorDacl");
    return(1);
  }

  /**************************************************************************\
  *
  * Check to see that SD is valid before attempting to write it to the file
  *
  \**************************************************************************/

  if (!IsValidSecurityDescriptor(psdAbsoluteSD))
  { PERR("IsValidSecurityDescriptor");
    return(1);
  }

  /**************************************************************************\
  *
  * Write SD to file system - first for A: then B:
  *
  \**************************************************************************/

  if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\A:"))
  { return(1);
  }

  if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\B:"))
  { return(1);
  }

  /**************************************************************************\
  *
  * only works for CDROM drives if you revoke the SeChangeNotify privilege
  * from Everyone in user manager
  *
  \**************************************************************************/
/*
  if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\E:"))
  { return(1);
  }
*/
  /**************************************************************************\
  *
  * Works for COM ports as well - commented out as this samples is floppy only
  *
  \**************************************************************************/
/*
  if (!WriteSD_ToA_File(psdAbsoluteSD,"COM1:"))
  { return(1);
  }
*/
  return(0);
}

/****************************************************************************\
*
* FUNCTION: WriteSD_ToA_File
*
\****************************************************************************/

BOOL WriteSD_ToA_File(PSECURITY_DESCRIPTOR psdAbsoluteSD, LPTSTR lpszFileName)
{
  DWORD dwErrorMode;
  BOOL  bStatus;

  /**************************************************************************\
  *
  * SetErrorMode so we don't get the error due to no floppy disk in the floppy
  *   drive
  *
  \**************************************************************************/

  dwErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);

  /**************************************************************************\
  *
  * Write SD to file system
  *
  \**************************************************************************/

  bStatus = SetFileSecurity(lpszFileName,
                            (SECURITY_INFORMATION)(DACL_SECURITY_INFORMATION),
                            psdAbsoluteSD);

  /**************************************************************************\
  *
  * SetErrorMode back to its previous value
  *
  \**************************************************************************/

  SetErrorMode(dwErrorMode);

  if (!bStatus)
  { if (ERROR_FILE_NOT_FOUND == GetLastError())
    { printf("\nAttempted to lock %s, but it was not found",lpszFileName);
    }
    else
    { PERR("SetFileSecurity");
      return(FALSE);
    }
  }

  return(TRUE);
}

/****************************************************************************\
*
* FUNCTION: DisplayHelp
*
\****************************************************************************/

VOID DisplayHelp(VOID)
{
  printf("\nTo run type SD_FLPPY and no (0) parameters.  Syntax:");
  printf("\n  SD_FLPPY");
  printf("\n          ");
  printf("\nExamples:");
  printf("\n  SD_FLPPY");
  printf("\n            Puts a DACL on A: and B: so that local Administrators");
  printf("\n              have all access and no one else has any access.");
  printf("\n            Since domain Administrators are by default members of");
  printf("\n              local Administrators, this will in many cases give");
  printf("\n              the desired result, but this utility is only one");
  printf("\n              example of the many possible interfaces that may be");
  printf("\n              desired.  Full source to this program is available,");
  printf("\n              so people may write different interfaces.");
  printf("\n            This .exe could be run from a logon script, or from");
  printf("\n              the Startup group.  An alternative approach is to");
  printf("\n              use the other version of this program.  The other");
  printf("\n              version is packaged as a service that applies the");
  printf("\n              DACLs when the machine boots up\n");
}


README.TXT

File Security

The SECFILE sample demonstrates the following security-related activities:

 - Constructing well-known security identifiers (SIDs)
 - Obtaining the current user's SID
 - Building a discretionary access control list (DACL)
 - Building a security descriptor (SD)

You should use this sample in conjunction with the Win32 Programmer's Reference.
Looking at the documentation and an example function call should make both
the documentation and the sample clearer.

This sample only works on an NTFS partition. No other file system supports
security. You can use File Manager or the Windows Explorer to check the
security on the file after you run the sample. One thing you may notice is
that the user "Everyone" has "Special" access instead of "Read" access.
That's because the system considers "Read" access to include both read and
execute rights. To modify the sample to give "Everyone" "Read" access,
change the statement:

    #define WORLD_ACCESS  GENERIC_READ

to the following:

    #define WORLD_ACCESS  (GENERIC_READ | GENERIC_EXECUTE)


SECFILE.C

//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1995 - 1998 Microsoft Corporation.All Rights Reserved.
//
// COMMENTS:
//  All buffers allocated in this sample come from HeapAlloc.  If
//  HeapAlloc fails, the PERR macro is not used because HeapAlloc does
//  not call SetLastError().
//

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

//
//  Constants and Macros
//
#define USER_ACCESSGENERIC_ALL
#define WORLD_ACCESSGENERIC_READ
#define FILENAME"testfile.txt"

#define PERR(s) fprintf(stderr, "%s(%d) %s : Error %d\n%s\n", \
    __FILE__, __LINE__, (s), GetLastError(), \
    GetLastErrorText())

//
//  Prototypes
//
PSID GetUserSid(void);
PSID CreateWorldSid(void);
LPSTR GetLastErrorText(void);

//
//  FUNCTION: main
//
//  PURPOSE: Driving routine for this sample.
//
//  PARAMETERS:
//none
//
//  RETURN VALUE:
//none
//
//  COMMENTS:
//
void main()
{
    PSID psidUser, psidEveryone;
    PACL pAcl;
    SECURITY_DESCRIPTOR sd;
    SECURITY_ATTRIBUTES sa;
    BOOL bRes;
    HANDLE hFile;
    DWORD dwBytesWritten, cbMsg, cbAcl;
    char szMsg[] = "Protected data\n";

    // Get the SIDs we'll need for the DACL
    //
    psidUser = GetUserSid();
    psidEveryone = CreateWorldSid();

    // Allocate space for the ACL
//For information about computing the size of the ACL, see
//the Win32 SDK reference entry for InitializeAcl()
    //
    cbAcl = GetLengthSid (psidUser) + GetLengthSid (psidEveryone) +
    sizeof(ACL) + (2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)));

    pAcl = (PACL) HeapAlloc(GetProcessHeap(), 0, cbAcl);
    if (NULL == pAcl) {
fprintf(stderr, "HeapAlloc failed.\n");
ExitProcess(EXIT_FAILURE);
    }

    bRes = InitializeAcl(pAcl,
    cbAcl,
    ACL_REVISION);
    if (FALSE == bRes) {
PERR("InitializeAcl");
ExitProcess(EXIT_FAILURE);
    }

    // Add Aces for User and World
    //
    bRes = AddAccessAllowedAce(pAcl,
    ACL_REVISION,
    USER_ACCESS,
    psidUser);
    if (FALSE == bRes) {
PERR("AddAccessAllowedAce");
ExitProcess(EXIT_FAILURE);
    }

    bRes = AddAccessAllowedAce(pAcl,
    ACL_REVISION,
    WORLD_ACCESS,
    psidEveryone);
    if (FALSE == bRes) {
PERR("AddAccessAllowedAce");
ExitProcess(EXIT_FAILURE);
    }

    // Put together the security descriptor
    //
    bRes = InitializeSecurityDescriptor(&sd,
SECURITY_DESCRIPTOR_REVISION);
    if (FALSE == bRes) {
PERR("InitializeSecurityDescriptor");
ExitProcess(EXIT_FAILURE);
    }

    bRes = SetSecurityDescriptorDacl(&sd,
TRUE,
pAcl,
FALSE);
    if (FALSE == bRes) {
PERR("SetSecurityDescriptorDacl");
ExitProcess(EXIT_FAILURE);
    }

    // Add the security descriptor to the sa structure
    //
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle = FALSE;

    // Generate the file using the security attributes that
    // we've assembled
    //
    hFile = CreateFile(FILENAME,
       GENERIC_READ |
       GENERIC_WRITE,
       0,
       &sa,
       CREATE_ALWAYS,
       FILE_ATTRIBUTE_NORMAL,
       NULL);
    if (INVALID_HANDLE_VALUE == hFile) {
PERR("CreateFile");
ExitProcess(EXIT_FAILURE);
    }

    cbMsg = lstrlen(szMsg);

    bRes = WriteFile(hFile,
     szMsg,
     cbMsg,
     &dwBytesWritten,
     NULL);
    if (FALSE == bRes) {
PERR("WriteFile");
ExitProcess(EXIT_FAILURE);
    }

    if (!CloseHandle(hFile)) {
PERR("CloseHandle");
ExitProcess(EXIT_FAILURE);
    }

    // Clean up
    //
    HeapFree(GetProcessHeap(), 0, pAcl);
    HeapFree(GetProcessHeap(), 0, psidUser);
    HeapFree(GetProcessHeap(), 0, psidEveryone);

    printf ("Created %s with special access.\n", FILENAME);

    ExitProcess(EXIT_SUCCESS);
}

//
//  FUNCTION: GetUserSid
//
//  PURPOSE: Obtains a pointer to the SID for the current user
//
//  PARAMETERS:
//none
//
//  RETURN VALUE:
//Pointer to the SID
//
//  COMMENTS:
//The SID buffer returned by this function is allocated with
//HeapAlloc and should be freed with HeapFree.
//
PSID GetUserSid()
{
    HANDLE hToken;
    BOOL bRes;
    DWORD cbBuffer, cbRequired;
    PTOKEN_USER pUserInfo;
    PSID pUserSid;

    // The User's SID can be obtained from the process token
    //
    bRes = OpenProcessToken(GetCurrentProcess(),
    TOKEN_QUERY,
    &hToken);
    if (FALSE == bRes) {
PERR("OpenProcessToken");
ExitProcess(EXIT_FAILURE);
    }

    // Set buffer size to 0 for first call to determine
    // the size of buffer we need.
    //
    cbBuffer = 0;
    bRes = GetTokenInformation(hToken,
TokenUser,
NULL,
cbBuffer,
&cbRequired);

    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
PERR("GetTokenInformation");
ExitProcess(EXIT_FAILURE);
    }

    // Allocate a buffer for our token user data
    //
    cbBuffer = cbRequired;
    pUserInfo = (PTOKEN_USER) HeapAlloc(GetProcessHeap(), 0, cbBuffer);
    if (NULL == pUserInfo) {
fprintf(stderr, "HeapAlloc failed.\n");
ExitProcess(EXIT_FAILURE);
    }

    // Make the "real" call
    //
    bRes = GetTokenInformation(hToken,
TokenUser,
pUserInfo,
cbBuffer,
&cbRequired);
    if (FALSE == bRes) {
PERR("GetTokenInformation");
ExitProcess(EXIT_FAILURE);
    }

    // Make another copy of the SID for the return value
    //
    cbBuffer = GetLengthSid(pUserInfo->User.Sid);

    pUserSid = (PSID) HeapAlloc(GetProcessHeap(), 0, cbBuffer);
    if (NULL == pUserSid) {
fprintf(stderr, "HeapAlloc failed.\n");
ExitProcess(EXIT_FAILURE);
    }

    bRes = CopySid(cbBuffer, pUserSid, pUserInfo->User.Sid);
    if (FALSE == bRes) {
PERR("CopySid");
ExitProcess(EXIT_FAILURE);
    }

    bRes = HeapFree(GetProcessHeap(), 0, pUserInfo);
    if (FALSE == bRes) {
fprintf(stderr, "HeapFree failed.\n");
ExitProcess(EXIT_FAILURE);
    }

    return pUserSid;
}

//
//  FUNCTION: CreateWorldSid
//
//  PURPOSE: Creates a SID that represents "Everyone"
//
//  PARAMETERS:
//none
//
//  RETURN VALUE:
//A pointer to the SID.
//
//  COMMENTS:
//The SID buffer returned by this function is allocated with
//HeapAlloc and should be freed with HeapFree.  I made a copy
//of the SID on the heap so that when the calling code is done
//with the SID it can use HeapFree().  This is consistent with
//other parts of this sample.
//
//The SID Authority and RID used here are defined in winnt.h.
//
PSID CreateWorldSid()
{
    SID_IDENTIFIER_AUTHORITY authWorld = SECURITY_WORLD_SID_AUTHORITY;
    PSID pSid, psidWorld;
    BOOL bRes;
    DWORD cbSid;

    bRes = AllocateAndInitializeSid(&authWorld,
    1,
    SECURITY_WORLD_RID,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    &psidWorld);
    if (FALSE == bRes) {
PERR("AllocateAndInitializeSid");
ExitProcess(EXIT_FAILURE);
    }

    // Make a copy of the SID using a HeapAlloc'd block for return
    //
    cbSid = GetLengthSid(psidWorld);

    pSid = (PSID) HeapAlloc(GetProcessHeap(), 0, cbSid);
    if (NULL == pSid) {
fprintf(stderr, "HeapAlloc failed.\n");
ExitProcess(EXIT_FAILURE);
    }

    bRes = CopySid(cbSid, pSid, psidWorld);
    if (FALSE == bRes) {
PERR("CopySid");
ExitProcess(EXIT_FAILURE);
    }

    FreeSid(psidWorld);

    return pSid;
}

//
//  FUNCTION: GetLastErrorText
//
//  PURPOSE: Retrieves the text associated with the last system error.
//
//  PARAMETERS:
//none
//
//  RETURN VALUE:
//A pointer to the error text.
//
//  COMMENTS:
//The contents of the returned buffer will only be valid until
//the next call to this routine.
//
LPSTR GetLastErrorText()
{
#define MAX_MSG_SIZE 256

    static char szMsgBuf[MAX_MSG_SIZE];
    DWORD dwError, dwRes;

    dwError = GetLastError ();

    dwRes = FormatMessage (
FORMAT_MESSAGE_FROM_SYSTEM,
                NULL,
                dwError,
                MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US),
szMsgBuf,
MAX_MSG_SIZE,
NULL);
    if (0 == dwRes) {
fprintf(stderr, "FormatMessage failed with %d\n", GetLastError());
ExitProcess(EXIT_FAILURE);
    }

    return szMsgBuf;
}


SECPERF.C

/*++

Module Name:

    secperf.c

Abstract:

    This sample illustrates how to regulate access to the performance data
    provided by the registry key HKEY_PERFORMANCE_DATA.

    The security on the following registry key dictates which users or groups
    can gain access to the performance data:

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib

    This sample opens the registry key for WRITE_DAC access, which allows
    for a new Dacl to be applied to the registry key.

    A Dacl is then built, which grants the following users access:

    Administrators are granted full control to allow for future updates to the
    security on the key and to allow for querying performance data.

    Interactively logged on users, through the well-known Interactive Sid,
    are granted KEY_READ access, which allows for querying performance
    data.

    The new Dacl is then applied to the registry key using the
    RegSetKeySecurity() Win32 API.

    This sample relies on the import library advapi32.lib.

Author:

    Scott Field (sfield)    19-Feb-96

--*/

#include <windows.h>
#include <stdio.h>

#define RTN_OK 0
#define RTN_ERROR 13

void
DisplayWinError(
    LPSTR szAPI,    // pointer to Ansi function name
    DWORD dwError   // DWORD WinError
    );

int
__cdecl
main(
    void
    )
{
    SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
    PSID pInteractiveSid = NULL;
    PSID pAdministratorsSid = NULL;
    SECURITY_DESCRIPTOR sd;
    PACL pDacl = NULL;
    DWORD dwAclSize;
    HKEY hKey;
    LONG lRetCode;
    BOOL bSuccess = FALSE; // assume this function fails

    //
    // open the performance key for WRITE_DAC access
    //
    lRetCode = RegOpenKeyEx(
        HKEY_LOCAL_MACHINE,
       TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib"),
        0,
        WRITE_DAC,
        &hKey
        );

    if(lRetCode != ERROR_SUCCESS) {
        DisplayWinError("RegOpenKeyEx", lRetCode);
        return RTN_ERROR;
    }

    //
    // prepare a Sid representing any Interactively logged-on user
    //
    if(!AllocateAndInitializeSid(
        &sia,
        1,
        SECURITY_INTERACTIVE_RID,
        0, 0, 0, 0, 0, 0, 0,
        &pInteractiveSid
        )) {
        DisplayWinError("AllocateAndInitializeSid", GetLastError());
        goto cleanup;
    }

    //
    // preprate a Sid representing the well-known admin group
    //
    if(!AllocateAndInitializeSid(
        &sia,
        2,
        SECURITY_BUILTIN_DOMAIN_RID,
        DOMAIN_ALIAS_RID_ADMINS,
        0, 0, 0, 0, 0, 0,
        &pAdministratorsSid
        )) {
        DisplayWinError("AllocateAndInitializeSid", GetLastError());
        goto cleanup;
    }

    //
    // compute size of new acl
    //
    dwAclSize = sizeof(ACL) +
        2 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
        GetLengthSid(pInteractiveSid) +
        GetLengthSid(pAdministratorsSid) ;

    //
    // allocate storage for Acl
    //
    pDacl = (PACL)HeapAlloc(GetProcessHeap(), 0, dwAclSize);
    if(pDacl == NULL) goto cleanup;

    if(!InitializeAcl(pDacl, dwAclSize, ACL_REVISION)) {
        DisplayWinError("InitializeAcl", GetLastError());
        goto cleanup;
    }

    //
    // grant the Interactive Sid KEY_READ access to the perf key
    //
    if(!AddAccessAllowedAce(
        pDacl,
        ACL_REVISION,
        KEY_READ,
        pInteractiveSid
        )) {
        DisplayWinError("AddAccessAllowedAce", GetLastError());
        goto cleanup;
    }

    //
    // grant the Administrators Sid KEY_ALL_ACCESS access to the perf key
    //
    if(!AddAccessAllowedAce(
        pDacl,
        ACL_REVISION,
        KEY_ALL_ACCESS,
        pAdministratorsSid
        )) {
        DisplayWinError("AddAccessAllowedAce", GetLastError());
        goto cleanup;
    }

    if(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
        DisplayWinError("InitializeSecurityDescriptor", GetLastError());
        goto cleanup;
    }

    if(!SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE)) {
        DisplayWinError("SetSecurityDescriptorDacl", GetLastError());
        goto cleanup;
    }

    //
    // apply the security descriptor to the registry key
    //
    lRetCode = RegSetKeySecurity(
        hKey,
        (SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
        &sd
        );

    if(lRetCode != ERROR_SUCCESS) {
        DisplayWinError("RegSetKeySecurity", lRetCode);
        goto cleanup;
    }

    bSuccess = TRUE; // indicate success

cleanup:

    RegCloseKey(hKey);
    RegCloseKey(HKEY_LOCAL_MACHINE);

    //
    // free allocated resources
    //
    if(pDacl != NULL)
        HeapFree(GetProcessHeap(), 0, pDacl);

    if(pInteractiveSid != NULL)
        FreeSid(pInteractiveSid);

    if(pAdministratorsSid != NULL)
        FreeSid(pAdministratorsSid);

    if(bSuccess) {
        printf("SUCCESS updating performance data security\n");
        return RTN_OK;
    } else {
        printf("ERROR updating performance data security\n");
        return RTN_ERROR;
    }
}

void
DisplayWinError(
    LPSTR szAPI,    // pointer to Ansi function name
    DWORD dwError   // DWORD WinError
    )
{
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    //
    // TODO get this fprintf out of here!
    //
    fprintf(stderr,"%s error!\n", szAPI);

    if(dwBufferLength=FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM,
            NULL,
            dwError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPSTR) &MessageBuffer,
            0,
            NULL
            ))
    {
        DWORD dwBytesWritten; // unused

        //
        // Output message string on stderr
        //
        WriteFile(
                GetStdHandle(STD_ERROR_HANDLE),
                MessageBuffer,
                dwBufferLength,
                &dwBytesWritten,
                NULL
                );

        //
        // free the buffer allocated by the system
        //
        LocalFree(MessageBuffer);
    }
}


README.TXT

Security Functions

SUMMARY
=======

The SIDCLN sample demonstrates some of the security functions. It
provides a sample utility that recovers on-disk resources that remain
allocated to deleted user accounts.

MORE INFORMATION
================

The on-disk resources recovered are:

 - Files that are still owned by accounts that have been deleted are
   assigned ownership to the account logged on when this sample is run.

 - ACEs for deleted accounts are deleted from the ACLs of files to which
   the deleted accounts had been granted authorizations (such as Read
   access).

It may be that running this sample as a utility has no practical value in
many environments, as the number of files belonging to deleted user accounts
will often be quite small and the number of bytes recovered on disk by
editing out ACEs for deleted accounts may not be worth the time it takes to
run this sample. This time can be significant when processing an entire hard
disk or partition.

Note: This sample is not a supported utility.

Usage
-----

You must log on using an account that has the privilege to take file
ownership and edit ACLs, such as Administrator.

The ACL editing part of this sample can only be exercised for files on an
NTFS partition.

Typical test scenario
---------------------

Create a user account or two and log on as each of these accounts in turn.
While logged on for each account, go to an NTFS partition and create a
couple of files so the test accounts each own a few files. Use the File
Manager or Windows Explorer to edit permissions for those files, so that
each test user has some authorities (such as Read) explicitly granted for
those files. Log on as Administrator and authorize each test user to a few
Administrator-owned files. Delete the test accounts. Run the sample in the
directories where you put the files that the test accounts own.


SIDCLEAN.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/****************************************************************************\
* MODULE:       sidclean.c
*
*               NT never deletes SIDs, so the name of this sample is most
*                 accurately be intrepreted as "Clean up SID ownership and
*                 ACE's that relate to SIDs that still (and always will)
*                 exist, but for which the corresponding user account has been
*                 deleted"
*
*
* PURPOSE:      Demonstrate some of the Win32 security api(s), and provide a
*                 sample of how a utility could be written that recovers
*                 on-disk resources remaining allocated to deleted user
*                 accounts.  The on-disk resources recovered are 1) Files that
*                 are still owned by accounts that have been deleted are
*                 assigned ownership to the account logged on when this sample
*                 is run, and 2) ACE's for deleted accounts are edited
*                 (deleted) out of the ACLs of files to which the deleted
*                 accounts had been granted authorizations (eg., Read access)
*
*               It may be that running this sample as a utility has no
*                 practical value in many environments, as the number of files
*                 belonging to deleted user accounts will often be quite
*                 small, and the number of bytes recovered on disk by editing
*                 out ACEs for deleted accounts may well not be worth the time
*                 it takes to run this sample.  The time it takes to run this
*                 sample may be quite significant when processing an entire
*                 hard disk or partition
*
*               This sample is not a supported utility
*
*
* TO RUN:       You must log on using an account, such as Administrator, that
*                 has the priviledges to take file ownership and edit ACls
*
*               The ACL editing part of this sample can only be excercised for
*                 files on a partition that has ACLs NT processes:  NTFS
*
*               Typical test scenario:  Create a user account or two, log on
*                 as each of these accounts in turn, while logged on for each
*                 account, go to an NTFS partition, create a couple of files
*                 so the test accounts each own a few files, use the file
*                 manager to edit permissions for those files so that each
*                 test user has some authorities (e.g., Read) explicitly
*                 granted for those files.  Logon as Administrator, authorize
*                 each test user to a few Administrator-owned files.  Delete
*                 the test accounts.  Run the sample in the directories where
*                 you put the files the test accounts owned or were authorized
*                 to
*
*
* OVERALL APPROACH: The command line interface is kept inflexible to simplify
*                 it's parsing in this sample.  The user must pass in a switch
*                 argument, a directory spec, and a file search pattern
*
*               The sample positions the current directory (of the process the
*                 sample runs in) to the dir spec, and uses FindFirstFile and
*                 FindNextFile to walk through the directory specified looking
*                 for files that match the file pattern specified
*
*               The switch argument can cause subdirectories to be searched
*                 recursively
*
*               The switch argument lets the user choose only to take
*                 ownerships, only to edit ACLs, do both, or do neither, in
*                 which case the sample merely reports on what ownerships
*                 would have been taken, and what ACE's would have been
*                 deleted
*
*               As the directories are walked, each file that matches the
*                 file pattern is processed right then
*
*               Note that we process files in a directory, and we also process
*                 the directory itself that contains the files.  We process
*                 directories because they can also be owned by deleted
*                 accounts, or could have ACEs that will no longer be used
*
*               Note also that we process all directories that we check for
*                 files, regardless of the spelling of the directory name
*
*               Counters are kept of file ownerships taken, ACEs deleted and
*                 total files checked, to print a summary line at the end of
*                 the run
*
*               The sample considers it perfectly acceptable if 0 files match
*                 the file pattern for the entire run
*
*
* FUNCTIONS:  DoMatchingFilesInOneDir
*
*               Look in one dir or sub-dir for files that match the file
*                 pattern.  For each match call DoOneFileOrDir
*
*             DoAllDirsInOneDir
*
*               For all the sub-dirs in a dir, set the current directory to be
*                 that directory, check for files that match the file pattern,
*                 and if any match, call DoMatchingFilesInOneDir to process
*                 those.  Then reset the current directory
*
*             GetFullFileOrDirName
*
*               Get the full name of the file or dir for simplified processing
*                 (and for display on the console)
*
*             DoOneFileOrDir
*
*               Get the file's SD (Security Descriptor), and call
*                 TakeOwnershipIfAppropriate and/or DeleteACEsAsAppropriate as
*                 needed
*
*             TakeOwnershipIfAppropriate
*
*               Get the owning SID of the file from the file's SD that
*                 DoOneFileOrDir passed in, check that SID to see if the
*                 account is deleted.  If so, edit into the file's SD a new
*                 owning SID (the SID of the process running the sample).
*                 Then write the modified file SD to disk
*
*             DeleteACEsAsAppropriate
*
*               Get the DACL of the file from the file's SD that
*                 DoOneFileOrDir passed in.  Walk through the ACE list for
*                 that DACL, checking each ACE to see what SID the ACE refers
*                 to.  For the SID referred to, check to see if the account is
*                 deleted.  If so, delete that ACE from the DACL.  When all
*                 ACE's have been examined, write the new DACL into the file's
*                 SD.  Then write the modified file SD to disk
*
*             GetProcessSid
*
*               Retrieve into a global variable the SID of the user account
*                 logged on when this sample is run.  This is the SID used by
*                 TakeOwnershipIfAppropriate
*
*               NOTE:  This routine has the notable side-effect on the access
*                 token of the curent process of enabling two privileges:
*                 SeTakeOwnershipPrivilege, and SeSecurityPrivilege.
*                 SeTakeOwnershipPrivilege is needed to ensure we can take
*                 ownership in spite of any DACL on the file.
*                 SeSecurityPrivilege is needed to work with SACLs
*
*             CrackArgs
*
*               Process the command line, cracking (parsing/decoding) the
*                 switch argument into boolean global variables (see below).
*                 Call DisplayHelp if anything illegal is found in the command
*                 line, or if the user asked for help
*
*             DisplayHelp
*
*               Display help text on the console
*
*
* GLOBAL VARS:
*             BOOL  bTakeOwnership
*             BOOL  bEditACLs
*             BOOL  bRecurse
*             BOOL  bJustCount
*
*               These store the values the user specified on the command
*                 line's first argument (the switches argument).
*                 Respectively, they record whether we are to do the
*                 processing to Take Ownerships, Edit ACLs, whether we are to
*                 recurse into all subdirectories, and whether we are just
*                 counting up what would be processed (in which case we take
*                 no ownerships and edit no ACLs)
*
*             DWORD dwFilesChecked
*             DWORD dwFilesOwned
*             DWORD dwACEsDeleted
*
*               These count, respecively, the total files we checked, the
*                 number of files we took ownership of (or would have if we
*                 had not been told only to count), and the number of ACEs we
*                 deleted (or would have if we had not been told only to
*                 count).  Note that the total number of files checked does
*                 not include files in the directories we process that do not
*                 match the file pattern
*
*               Note, however, that we process directories regardless of
*                 whether they match the file pattern
*
*             UCHAR ucProcessSIDBuf
*             PSID  psidProcessOwnerSID
*
*               These store the SID of the account logged on as this sample
*                 runs, and a pointer to that SID
*
\****************************************************************************/

/****************************************************************************\
*  INCLUDES, DEFINES, TYPEDEFS
\****************************************************************************/
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define PERR(api) printf("%s: Error %d from %s on line %d\n",  \
    __FILE__, GetLastError(), api, __LINE__);
#define PMSG(msg) printf("%s line %d: %s\n",  \
    __FILE__, __LINE__, msg);

#define PrintAppStyleAPIError(ApiTxt,MsgTxt) {                     \
  DWORD dwLastError;                                               \
  dwLastError = GetLastError();                                    \
  switch (dwLastError)                                             \
  { case ERROR_FILE_NOT_FOUND :                                    \
      printf("\nFile not found (%s) line %d",MsgTxt,__LINE__);     \
      break;                                                       \
    case ERROR_INVALID_NAME   :                                    \
      printf("\nInvalid name (%s) line %d",MsgTxt,__LINE__);       \
      break;                                                       \
    case ERROR_PATH_NOT_FOUND :                                    \
      printf("\nError path not found (%s) line %d",MsgTxt,__LINE__); \
      break;                                                       \
    case ERROR_SHARING_VIOLATION :                                 \
      printf("\nSharing violation - shut down net and/or stop other sessions (%s) line %d",MsgTxt,__LINE__); \
      break;                                                       \
    case ERROR_ACCESS_DENIED  :                                    \
      printf("\nAccess denied (%s) line %d",MsgTxt,__LINE__);      \
      break;                                                       \
    default                   :                                    \
      printf("\n" #ApiTxt " - unexpected return code=%d (%s) line %d",dwLastError,MsgTxt,__LINE__); \
      break;                                                       \
  }                                                                \
  }

/****************************************************************************\
* GLOBAL VARIABLES
\****************************************************************************/

BOOL  bTakeOwnership  = FALSE;
BOOL  bEditACLs       = FALSE;
BOOL  bRecurse        = FALSE;
BOOL  bJustCount      = FALSE;

DWORD dwFilesChecked  = 0;
DWORD dwFilesOwned    = 0;
DWORD dwACEsDeleted   = 0;

PSID  psidProcessOwnerSID;

/****************************************************************************\
* FUNCTION PROTOTYPES
\****************************************************************************/

BOOL DoMatchingFilesInOneDir(HANDLE          hFound,
                             WIN32_FIND_DATA ffdFoundData);
BOOL DoAllDirsInOneDir(char *FilePattern);
BOOL GetFullFileOrDirName(LPTSTR lpszFileName);
BOOL DoOneFileOrDir(LPTSTR lpszFullName);
BOOL TakeOwnershipIfAppropriate(PSECURITY_DESCRIPTOR psdFileSD,
                                LPTSTR  lpszFullName);
BOOL DeleteACEsAsAppropriate   (PSECURITY_DESCRIPTOR psdFileSD,
                                LPTSTR  lpszFullName);
BOOL GetProcessSid(VOID);
BOOL CrackArgs(UINT argc, char *argv[]);
VOID DisplayHelp(VOID);

/****************************************************************************\
*
* FUNCTION: Main
*
\****************************************************************************/

UINT main(UINT argc, char *argv[])
{
  WIN32_FIND_DATA ffdFoundData;
  HANDLE          hFound;
  #define                   SZ_NAME_BUF MAX_PATH
  UCHAR           ucPathBuf[SZ_NAME_BUF];
  LPTSTR          lpszFullName = (LPTSTR)&ucPathBuf;

  /**************************************************************************\
  *
  * Store the process's SID in a global variable for later use (in taking
  *   ownership).
  *
  \**************************************************************************/

  if (!GetProcessSid())
  { PERR("Can't proceed without process SID - see earlier error messages");
    return(1);
  }

  if (!CrackArgs(argc,argv))
    return(1);

  /**************************************************************************\
  *
  * CrackArgs has set our global processing switches, and proven argv[2] and
  *   argv[3] are our non-blank dir-spec and file-pattern strings.  Now we
  *   must see that the file-spec is acceptable to the Win32 api's.  Argv[2]
  *   is the file-spec to pass to SetCurrentDirectory, and argv[3] is the
  *   file-pattern to pass to FindFirstFile
  *
  * First we have to expand the dir-spec in argv[2], because if we set the
  *   current directory to it before expansion,the expansion will have a
  *   different result if argv[2] is something like ..\..
  *
  \**************************************************************************/

  strcpy(lpszFullName,argv[2]);

  if (!GetFullFileOrDirName(lpszFullName))
  { PERR("Failed to expand to full name the 2nd argument (directory specification)");
    return(1);
  }

  /**************************************************************************\
  *
  * Now we pass the un-expanded argv[2] to SetCurrentDirectory for validity
  *   checking.  GetFullPathName (called by GetFullFileOrDirName) does not
  *   validity check
  *
  \**************************************************************************/

  if (!SetCurrentDirectory(argv[2]))
  { PrintAppStyleAPIError(SetCurrentDirectory,"2nd argument (directory specification)");
    return(1);
  }

  /**************************************************************************\
  *
  * We begin processing with the current directory, using the expanded form we
  *   got before.  We have to use the expanded form, because if we set to
  *   ..\.. and then try to process the string ..\.. as a dir name, instead of
  *   processing the dir two levels up from where we are we'll process the dir
  *   four levels up
  *
  \**************************************************************************/

  if (!DoOneFileOrDir(lpszFullName))
    return(1);

  /**************************************************************************\
  *
  * It's OK to get no hits.  The files-checked counter will show how many
  *   files we looked at, and it's OK to look at 0
  *
  * On the else branch, Argv[3] has been verified, and we have a good handle.
  *   We now pass to DoMatchingFilesInOneDir for processing the handle and
  *   found data we just got from FindFirstFile
  *
  \**************************************************************************/

  hFound = FindFirstFile(argv[3],
                         (LPWIN32_FIND_DATA)&ffdFoundData);
  if ((HANDLE)(-1) == hFound)
  { if (GetLastError() != ERROR_FILE_NOT_FOUND)
    { PrintAppStyleAPIError(FindFirstFile,"3rd argument");
      return(1);
    }
  }
  else if (!DoMatchingFilesInOneDir(hFound,ffdFoundData))
    return(1);

  /**************************************************************************\
  *
  * Pass the original file pattern for recursive calling to DoAllDirsInOneDir
  *
  \**************************************************************************/

  if (!DoAllDirsInOneDir(argv[3]))
    return(1);

  if (bJustCount)
    printf("\nChecked %d files, would have taken ownership of %d files, would have deleted %d ACEs\n",
           dwFilesChecked,dwFilesOwned,dwACEsDeleted);
  else
    printf("\nChecked %d files, took ownership of %d files, deleted %d ACEs\n",
           dwFilesChecked,dwFilesOwned,dwACEsDeleted);

  free(psidProcessOwnerSID);

  return(0);
}

/****************************************************************************\
*
* FUNCTION: DoMatchingFilesInOneDir
*
\****************************************************************************/

BOOL DoMatchingFilesInOneDir(HANDLE          hFound,
                             WIN32_FIND_DATA ffdFoundData)
{
  BOOL bDoneWithHandle = FALSE;

  /**************************************************************************\
  *
  * Process all files referred to by the handle, but not including
  *   directories, because directories are handled with separate calls to
  *   DoOneFileOrDir.  Such separate calls are made as we are setting the
  *   current directory to be the directory to be processed
  *
  \**************************************************************************/

  while (!bDoneWithHandle)
  {
    if (!(FILE_ATTRIBUTE_DIRECTORY & ffdFoundData.dwFileAttributes))
    {
      if (!DoOneFileOrDir(ffdFoundData.cFileName))
        return(FALSE);
    }

    if (!FindNextFile(hFound,
                      (LPWIN32_FIND_DATA)&ffdFoundData))
      if (GetLastError() == ERROR_NO_MORE_FILES)
        bDoneWithHandle = TRUE;
      else
      { PrintAppStyleAPIError(FindNextFile,"on FindNext");
        return(FALSE);
      }
  }
  return TRUE;
}

/****************************************************************************\
*
* FUNCTION: DoAllDirsInOneDir
*
\****************************************************************************/

BOOL DoAllDirsInOneDir(char *FilePattern)
{
  HANDLE          hFound;
  WIN32_FIND_DATA ffdFoundData;
  BOOL            bDoneWithHandle = FALSE;

  /**************************************************************************\
  *
  * If not recursing into dirs, simply return
  *
  \**************************************************************************/

  if (!bRecurse)
    return TRUE;

  /**************************************************************************\
  *
  * Since we are recursing, get a handle that points to entire directory, and
  *   walk the handle picking off only directories to recurse into
  *
  \**************************************************************************/

  hFound = FindFirstFile("*.*",
                         (LPWIN32_FIND_DATA)&ffdFoundData);
  if ((HANDLE)(-1) == hFound)
  { PrintAppStyleAPIError(FindFirstFile,"on dir *.* FindFirst");
    return(FALSE);
  }

  while (!bDoneWithHandle)
  {
    /************************************************************************\
    *
    * We only do dirs here, and we only do directories with textual names
    *   (i.e., not "." and not "..")
    *
    \************************************************************************/

    if (   (FILE_ATTRIBUTE_DIRECTORY & ffdFoundData.dwFileAttributes)
        && (0 != strcmp("." ,ffdFoundData.cFileName))
        && (0 != strcmp("..",ffdFoundData.cFileName)))
    {
      HANDLE          hFile2;
      WIN32_FIND_DATA ffdFound2;

      /**********************************************************************\
      *
      * We begin processing the new current directory by processing it itself,
      *   then setting the current dir to be the dir itself
      *
      \**********************************************************************/

      if (!DoOneFileOrDir(ffdFoundData.cFileName))
        return(FALSE);

      if (!SetCurrentDirectory(ffdFoundData.cFileName))
      { PrintAppStyleAPIError(SetCurrentDirectory,"recursive set");
        return(FALSE);
      }

      /**********************************************************************\
      *
      * It's OK to get no hits.  The files-checked counter will show how many
      *   files we looked at, and it's OK to look at 0
      *
      \**********************************************************************/

      hFile2 = FindFirstFile(FilePattern,
                             (LPWIN32_FIND_DATA)&ffdFound2);
      if ((HANDLE)(-1) == hFile2)
      { if (GetLastError() != ERROR_FILE_NOT_FOUND)
        { PrintAppStyleAPIError(FindFirstFile,"during recursion");
          return(FALSE);
        }
      }
      else if (!DoMatchingFilesInOneDir(hFile2,ffdFound2))
        return(FALSE);

      if (!DoAllDirsInOneDir(FilePattern))
        return(FALSE);

      if (!SetCurrentDirectory(".."))
      { PrintAppStyleAPIError(SetCurrentDirectory,"un-recursive set");
        return(FALSE);
      }
    }

    /************************************************************************\
    *
    * Get next recursion candidate (file or dir at this point, however at loop
    *   top files are screened out)
    *
    \************************************************************************/

    if (!FindNextFile(hFound,
                      (LPWIN32_FIND_DATA)&ffdFoundData))
      if (GetLastError() == ERROR_NO_MORE_FILES)
        bDoneWithHandle = TRUE;
      else
      { PrintAppStyleAPIError(FindNextFile,"on dir *.* FindNext");
        return(FALSE);
      }
  }
  return(TRUE);
}

/****************************************************************************\
*
* FUNCTION: GetFullFileOrDirName
*
\****************************************************************************/

BOOL GetFullFileOrDirName(LPTSTR lpszFileName)
{
  UCHAR   ucPathBuf[SZ_NAME_BUF];
  DWORD   dwSzReturned;
  LPTSTR  lpszLastNamePart;
  LPTSTR  lpszFullName;

  dwSzReturned = GetFullPathName
                   (lpszFileName,
                    (DWORD)SZ_NAME_BUF,
                    (LPTSTR)&ucPathBuf,
                    (LPTSTR *)&lpszLastNamePart);
  if (0 == dwSzReturned)
    switch (GetLastError())
    { case ERROR_INVALID_NAME   :
        printf("\nError invalid file full-name (on GetFullPathName)");
        return(FALSE);
      default                   :
        PERR("GetFullPathName - unexpected return code");
        return(FALSE);
    }

  if (dwSzReturned > SZ_NAME_BUF)
  { PERR("GetFullPathName - buffer too small");
    return(FALSE);
  }

  lpszFullName = CharLower((LPTSTR)&ucPathBuf);

  if (!lpszFullName)
  { PERR("CharLower failure");
    return(FALSE);
  }

  /**************************************************************************\
  *
  * Copy the expanded and upper-case-shifted name to the buffer pointed to by
  *   the input argument
  *
  \**************************************************************************/

  strcpy(lpszFileName,lpszFullName);
}

/****************************************************************************\
*
* FUNCTION: DoOneFileOrDir
*
\****************************************************************************/

BOOL DoOneFileOrDir(LPTSTR lpszFullName)
{
  #define                                 SZ_REL_SD_BUF 1000
  #define                                 SZ_ABS_SD_BUF  500
  #define                                 SZ_DACL_BUF    500
  #define                                 SZ_SACL_BUF    500
  #define                                 SZ_SID_OWN_BUF 500
  #define                                 SZ_SID_PG_BUF  500
  UCHAR                ucBuf             [SZ_REL_SD_BUF];
  UCHAR                ucBufAbs          [SZ_ABS_SD_BUF];
  UCHAR                ucBufDacl         [SZ_DACL_BUF];
  UCHAR                ucBufSacl         [SZ_SACL_BUF];
  UCHAR                ucBufCtrl         [sizeof(PSECURITY_DESCRIPTOR_CONTROL)];
  UCHAR                ucBufSidOwn       [SZ_SID_OWN_BUF];
  UCHAR                ucBufSidPG        [SZ_SID_PG_BUF];
  DWORD                dwSDLength       = SZ_REL_SD_BUF;
  DWORD                dwDACLLength     = SZ_DACL_BUF;
  DWORD                dwSACLLength     = SZ_SACL_BUF;
  DWORD                dwSidOwnLength   = SZ_SID_OWN_BUF;
  DWORD                dwSidPGLength    = SZ_SID_PG_BUF;
  DWORD                dwSDLengthNeeded;
  PSECURITY_DESCRIPTOR psdSrelFileSD    = (PSECURITY_DESCRIPTOR)&ucBuf;
  PSECURITY_DESCRIPTOR psdAbsFileSD     = (PSECURITY_DESCRIPTOR)&ucBufAbs;
  PSECURITY_DESCRIPTOR_CONTROL psdcCtrl = (PSECURITY_DESCRIPTOR_CONTROL)&ucBufCtrl;
  PACL                 paclDacl         = (PACL)&ucBufDacl;
  PACL                 paclSacl         = (PACL)&ucBufSacl;
  PSID                 psidSidOwn       = (PSID)&ucBufSidOwn;
  PSID                 psidSidPG        = (PSID)&ucBufSidPG;
  BOOL                 bDaclPresent;
  BOOL                 bDaclDefaulted;
  BOOL                 bSaclPresent;
  BOOL                 bSaclDefaulted;
  BOOL                 bOwnerDefaulted;
  BOOL                 bGroupDefaulted;
  BOOL                 bSDSelfRelative;
  DWORD                dwRevision;

  if (!GetFullFileOrDirName(lpszFullName))
    return(FALSE);

  /**************************************************************************\
  *
  * Now the input argument's name is accurate:  it is expanded and lower-case
  *
  \**************************************************************************/

  printf("\nChecking %s",lpszFullName);

  dwFilesChecked++;

  if (!GetFileSecurity
        (lpszFullName,
         (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION
                               | GROUP_SECURITY_INFORMATION
                               | DACL_SECURITY_INFORMATION
                               | SACL_SECURITY_INFORMATION),
         psdSrelFileSD,
         dwSDLength,
         (LPDWORD)&dwSDLengthNeeded))
  { PERR("GetFileSecurity");
    return(FALSE);
  }

  /**************************************************************************\
  *
  * This validity check is here for demonstration pruposes.  It's not likely a
  *   real app would need to check the validity of this returned SD.  The
  *   validity check APIs are more intended to check validity after app code
  *   has manipulated the structure and is about to hand it back to the system
  *
  \**************************************************************************/

  if (!IsValidSecurityDescriptor(psdSrelFileSD))
  { PERR("IsValidSecurityDescriptor said bad SD");
    return(FALSE);
  }

  /**************************************************************************\
  *
  *  Build File SD in absolute format for potential later modification
  *
  *  First Initialize a new SD, which is by definition in absolute format
  *
  *  Then Set in the fields from the relative format SD we just fetched
  *
  \**************************************************************************/

  if (!InitializeSecurityDescriptor(psdAbsFileSD,
                 SECURITY_DESCRIPTOR_REVISION))
  { PERR("InitializeSecurityDescriptor");
    return FALSE;
  }

  /**************************************************************************\
  *
  * Get Control from relative format File SD
  *
  * This control info isn't much queried in the code that follows, as the
  *   Get/Set calls are more convienent in this case, but it does give us a
  *   change to verify that the SD is in relative format
  *
  \**************************************************************************/

  if (!GetSecurityDescriptorControl(psdSrelFileSD,
          psdcCtrl,
          &dwRevision))
  { PERR("GetSecurityDescriptorControl");
    return FALSE;
  }

  bSDSelfRelative = (SE_SELF_RELATIVE & *psdcCtrl);

  /**************************************************************************\
  *
  * Set DACL into absolute format File SD
  *
  * Note that it is possible that a NULL DACL has been explictly specified.
  *   If so the Get/Set call pair will correctly map that into the absolute
  *   format SD
  *
  * The next if statement isn't necessary, it simply shows the relationship
  *   between SE_DACL_PRESENT and SE_DACL_DEFAULTED, and lets you trace
  *   through with the debugger

*
  \**************************************************************************/

  if (bDaclPresent = (SE_DACL_PRESENT   & *psdcCtrl))
  {                // SE_DACL_DEFAULTED ignored if SE_DACL_PRESENT not set
    bDaclDefaulted = (SE_DACL_DEFAULTED & *psdcCtrl);
  }
  else
  { // No DACL at all
  }

  if (!GetSecurityDescriptorDacl(psdSrelFileSD,
          &bDaclPresent,      // fDaclPresent flag
          &paclDacl,
          &bDaclDefaulted))   // is/is not a default DACL
  { PERR("GetSecurityDescriptorDacl");
    return FALSE;
  }
  if (!SetSecurityDescriptorDacl(psdAbsFileSD,
          bDaclPresent,       // fDaclPresent flag
          paclDacl,
          bDaclDefaulted))    // is/is not a default DACL
  { PERR("SetSecurityDescriptorDacl");
    return FALSE;
  }

  /**************************************************************************\
  *
  * Set SACL into absolute format File SD
  *
  * Note that it is possible that a NULL SACL has been explictly specified.
  *   If so the Get/Set call pair will correctly map that into the absolute
  *   format SD
  *
  * The next if statement isn't necessary, it simply shows the relationship
  *   between SE_SACL_PRESENT and SE_SACL_DEFAULTED, and lets you trace
  *   through with the debugger
  *
  \**************************************************************************/

  if (bSaclPresent = (SE_SACL_PRESENT   & *psdcCtrl))
  {                // SE_SACL_DEFAULTED ignored if SE_SACL_PRESENT not set
    bSaclDefaulted = (SE_SACL_DEFAULTED & *psdcCtrl);
  }
  else
  { // No SACL at all
  }

  if (!GetSecurityDescriptorSacl(psdSrelFileSD,
          &bSaclPresent,      // fSaclPresent flag
          &paclSacl,
          &bSaclDefaulted))   // is/is not a default SACL
  { PERR("GetSecurityDescriptorSacl");
    return FALSE;
  }
  if (!SetSecurityDescriptorSacl(psdAbsFileSD,
          bSaclPresent,       // fSaclPresent flag
          paclSacl,
          bSaclDefaulted))    // is/is not a default SACL
  { PERR("SetSecurityDescriptorSacl");
    return FALSE;
  }

  /**************************************************************************\
  *
  * Set Owner into absolute format File SD
  *
  * The next if statement isn't necessary, it simply let's you trace through
  *   with the debugger
  *
  \**************************************************************************/

  bOwnerDefaulted = (SE_OWNER_DEFAULTED & *psdcCtrl);

  if (!GetSecurityDescriptorOwner(psdSrelFileSD,
          &psidSidOwn,
          &bOwnerDefaulted))   // is/is not a default Owner
  { PERR("GetSecurityDescriptorOwner");
    return FALSE;
  }
  if (!SetSecurityDescriptorOwner(psdAbsFileSD,
          psidSidOwn,
          bOwnerDefaulted))    // is/is not a default Owner
  { PERR("SetSecurityDescriptorOwner");
    return FALSE;
  }

  /**************************************************************************\
  *
  * Set Group into absolute format File SD
  *
  * The next if statement isn't necessary, it simply let's you trace through
  *   with the debugger
  *
  \**************************************************************************/

  bGroupDefaulted = (SE_GROUP_DEFAULTED & *psdcCtrl);

  if (!GetSecurityDescriptorGroup(psdSrelFileSD,
          &psidSidOwn,
          &bGroupDefaulted))   // is/is not a default Group
  { PERR("GetSecurityDescriptorGroup");
    return FALSE;
  }
  if (!SetSecurityDescriptorGroup(psdAbsFileSD,
          psidSidOwn,
          bGroupDefaulted))    // is/is not a default Group
  { PERR("SetSecurityDescriptorGroup");
    return FALSE;
  }

  /**************************************************************************\
  *
  * This validity check is here for demonstration pruposes.  It's not likely a
  *   real app would need to check the validity of the SD after it was just
  *   built into absolute format.  The validity check APIs are more intended
  *   to check validity after app code has manipulated the structure and is
  *   about to hand it back to the system
  *
  * One thing to notice is that IsValidSecurityDescriptor will succeed on both
  *   self-relative and absolute format SDs.  However, some other api's, such
  *   as SetSecurityDescriptorOwner, require the SD to be in a certain format,
  *   and will give a return code of Invalid SD if the SD passed to the api is
  *   valid, but in the wrong format.  In other words, when an api such as
  *   SetSecurityDescriptorOwner gives the retun code Invalid SD, this doesn't
  *   mean the SD passed in was necessarily invalid.  It might have been in
  *   the wrong format
  *
  \**************************************************************************/

  if (!IsValidSecurityDescriptor(psdAbsFileSD))
  { PERR("IsValidSecurityDescriptor said bad SD");
    return(FALSE);
  }

  if (bTakeOwnership)
    if (!TakeOwnershipIfAppropriate(psdAbsFileSD,lpszFullName))
      return(FALSE);

  if (bEditACLs)
    if (!DeleteACEsAsAppropriate   (psdAbsFileSD,lpszFullName))
      return(FALSE);

  return(TRUE);
}

/****************************************************************************\
*
* FUNCTION: TakeOwnershipIfAppropriate
*
\****************************************************************************/

BOOL TakeOwnershipIfAppropriate(PSECURITY_DESCRIPTOR psdFileSD,
                                LPTSTR  lpszFullName)
{
  PSID psidFileOwnerSID;
  {
    BOOL  bOwnerDefaulted;

    if (!GetSecurityDescriptorOwner
           (psdFileSD,
            (PSID *)&psidFileOwnerSID,
            (LPBOOL)&bOwnerDefaulted))
    { PERR("GetSecurityDescriptorOwner");
      return(FALSE);
    }

    /************************************************************************\
    *
    * This validity check is here for demonstration pruposes.  It's not likely
    *   a real app would need to check the validity of this returned SID.  The
    *   validity check APIs are more intended to check validity after app code
    *   has manipulated the structure and is about to hand it back to the
    *   system
    *
    \************************************************************************/

    if (!IsValidSid(psidFileOwnerSID))
    { PERR("IsValidSid said bad SID!");
      return(FALSE);
    }
  }

  {
    DWORD        dwLastError   = NO_ERROR;
    #define                      SZ_ACCT_NAME_BUF 1000
    UCHAR        ucNameBuf      [SZ_ACCT_NAME_BUF];
    DWORD        dwNameLength  = SZ_ACCT_NAME_BUF;
    #define                      SZ_DMN_NAME_BUF  1000
    UCHAR        ucDomainNmBuf  [SZ_DMN_NAME_BUF ];
    DWORD        dwDNameLength = SZ_DMN_NAME_BUF ;
    SID_NAME_USE peAcctNameUse;

    if (!LookupAccountSid
           ((LPTSTR)"",             // Look on local machine
           psidFileOwnerSID,
           (LPTSTR)&ucNameBuf,
           (LPDWORD)&dwNameLength,
           (LPTSTR)&ucDomainNmBuf,
           (LPDWORD)&dwDNameLength,
           (PSID_NAME_USE)&peAcctNameUse))
    { dwLastError = GetLastError();
      if (ERROR_NONE_MAPPED != dwLastError)
      { PERR("LookupAccountSID");
        return(FALSE);
      }
    }

    /************************************************************************\
    *
    * If deleted account, take ownership.  This routine's caller checked the
    *   global switches that said we are in take ownership mode
    *
    * In some cases, the account lookup will fail with ERROR_NONE_MAPPED,
    *   meaning there is no deleted account mapped to the SID, in which case
    *   we also take ownership
    *
    *   We check that first to avoid referencing peAcctNameUse, which in that
    *     case is not set
    *
    \************************************************************************/

    if (  (ERROR_NONE_MAPPED == dwLastError)
       || (SidTypeDeletedAccount == peAcctNameUse))
    {
      dwFilesOwned++;

      if (bJustCount)
      { printf(" - would have taken ownership");
        return(TRUE);
      }
      else
      {
        /********************************************************************\
        *
        * Modify the SD in virtual memory.  No check on the new owning SID
        *   here, because we validity checked it when we fetched it in
        *   GetProcessSid
        *
        \********************************************************************/

        if (!SetSecurityDescriptorOwner
               (psdFileSD,
                psidProcessOwnerSID,
                FALSE))               // New owner explicitly specified
        { PERR("SetSecurityDescriptorOwner");
          return(FALSE);
        }

        /********************************************************************\
        *
        *  This validity check is something a real app might actually like to
        *    do.  We manupulated the SD, so before we write it back out to the
        *    file system, a check is worth considering.
        *
        \********************************************************************/

        if (!IsValidSecurityDescriptor(psdFileSD))
        { PERR("IsValidSecurityDescriptor said bad SD");
          return(FALSE);
        }

        /********************************************************************\
        *
        * Modify the SD on the hard disk
        *
        \********************************************************************/

        if (!SetFileSecurity
               (lpszFullName,
                (SECURITY_INFORMATION)OWNER_SECURITY_INFORMATION,
                psdFileSD))
        { PERR("SetFileSecurity");
          return(FALSE);
        }

        printf(" - took ownership");
      }
    }
  }

  return(TRUE);

}

/****************************************************************************\
*
* FUNCTION: DeleteACEsAsAppropriate
*
\****************************************************************************/

BOOL DeleteACEsAsAppropriate(PSECURITY_DESCRIPTOR psdFileSD,
                             LPTSTR  lpszFullName)
{
  PACL                 paclFile;
  BOOL                 bHasACL;
  BOOL                 bOwnerDefaulted;
  DWORD                dwAcl_i;
  DWORD                dwACEsDeletedBeforeNow;

  ACL_SIZE_INFORMATION                      asiAclSize;
  DWORD                dwBufLength = sizeof(asiAclSize);
  ACCESS_ALLOWED_ACE   *paaAllowedAce;

  if (!GetSecurityDescriptorDacl(psdFileSD,
                                 (LPBOOL)&bHasACL,
                                 (PACL *)&paclFile,
                                 (LPBOOL)&bOwnerDefaulted))
  { PERR("GetSecurityDescriptorDacl");
    return(FALSE);
  }

  if (!bHasACL)  // No ACL to process, so OK, return
    return(TRUE);

  /**************************************************************************\
  *
  * This validity check is here for demonstration pruposes.  It's not likely a
  *   real app would need to check the validity of this returned ACL.  The
  *   validity check APIs are more intended to check validity after app code
  *   has manipulated the structure and is about to hand it back to the system
  *
  \**************************************************************************/

  if (!IsValidAcl(paclFile))
  { PERR("IsValidAcl said bad ACL!");
    return(FALSE);
  }

  if (!GetAclInformation(paclFile,
                         (LPVOID)&asiAclSize,
                         (DWORD)dwBufLength,
                         (ACL_INFORMATION_CLASS)AclSizeInformation))
  { PERR("GetAclInformation");
    return(FALSE);
  }

  dwACEsDeletedBeforeNow = dwACEsDeleted;

  /**************************************************************************\
  *
  * We loop through in reverse order, because that's simpler, given that we
  *   potentially delete ACEs as we loop through.  If started at 0 and went
  *   up, if we deleted the 0th ACE, then the 1th ACE would become the 0th,
  *   and we'd have to check the 0th ACE again
  *
  \**************************************************************************/

  for (dwAcl_i = asiAclSize.AceCount-1;  ((int)dwAcl_i) >= 0;  dwAcl_i--)
  {
    /************************************************************************\
    *
    * It doesn't matter for this sample that we don't yet know the ACE type,
    *   because they all start with the header field and that's what we need
    *
    \************************************************************************/

    if (!GetAce(paclFile,
                dwAcl_i,
                (LPVOID *)&paaAllowedAce))
    { PERR("GetAce");
      return(FALSE);
    }

    /************************************************************************\
    *
    * There are only four Ace Types pre-defined, so this next check is
    *   redundant in a real app, but useful as a sanity check and a
    *   demonstration in a sample
    *
    \************************************************************************/

    if (!( (paaAllowedAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
         ||(paaAllowedAce->Header.AceType == ACCESS_DENIED_ACE_TYPE )
         ||(paaAllowedAce->Header.AceType == SYSTEM_AUDIT_ACE_TYPE  )
         ||(paaAllowedAce->Header.AceType == SYSTEM_ALARM_ACE_TYPE  )))
    { PERR("Invalid AceType");
      return(FALSE);
    }

    { // Find SID of ACE, check if acct deleted

      UCHAR        ucNameBuf      [SZ_ACCT_NAME_BUF];
      DWORD        dwNameLength  = SZ_ACCT_NAME_BUF;
      UCHAR        ucDomainNmBuf  [SZ_DMN_NAME_BUF];
      DWORD        dwDNameLength = SZ_DMN_NAME_BUF;
      SID_NAME_USE peAcctNameUse;
      DWORD        dwLastError   = NO_ERROR;

      /**********************************************************************\
      *
      * This validity check is here for demonstration pruposes.  It's not
      *   likely a real app would need to check the validity of the SID
      *   contained in the returned ACL.  The validity check APIs are more
      *   intended to check validity after app code has manipulated the
      *   structure and is about to hand it back to the system
      *
      \**********************************************************************/

      if (!IsValidSid((PSID)&(paaAllowedAce->SidStart)))
      { PERR("IsValidSid said bad SID!");
        return(FALSE);
      }

      if (!LookupAccountSid
             ((LPTSTR)"",         // Look on local machine
             (PSID)&(paaAllowedAce->SidStart),
             (LPTSTR)&ucNameBuf,
             (LPDWORD)&dwNameLength,
             (LPTSTR)&ucDomainNmBuf,
             (LPDWORD)&dwDNameLength,
             (PSID_NAME_USE)&peAcctNameUse))
      { dwLastError = GetLastError();
        if (ERROR_NONE_MAPPED != dwLastError)
        { PERR("LookupAccountSID");
          return(FALSE);
        }
      }

      if (  (ERROR_NONE_MAPPED == dwLastError)
         || (SidTypeDeletedAccount == peAcctNameUse))
      {
        dwACEsDeleted++;

        if (bJustCount)
        { printf(" - would have edited ACL");
          return(TRUE);
        }

        if (!DeleteAce(paclFile,dwAcl_i))
        { PERR("DeleteAce");
          return(FALSE);
        }
      }
    }
  }

  if (dwACEsDeletedBeforeNow < dwACEsDeleted)
  {
    /************************************************************************\
    *
    * This validity check is something a real app might actually like to do.
    *   We manupulated the ACL, so before we write it back into an SD, a check
    *   is worth considering
    *
    \************************************************************************/

    if (!IsValidAcl(paclFile))
    { PERR("IsValidAcl said bad ACL!");
      return(FALSE);
    }

    /************************************************************************\
    *
    * Modify the SD in virtual memory
    *
    \************************************************************************/

    if (!SetSecurityDescriptorDacl
           (psdFileSD,
            TRUE,                 // Yes, set the DACL
            paclFile,
            FALSE))               // New DACL explicitly specified
    { PERR("SetSecurityDescriptorDacl");
      return(FALSE);
    }

    /************************************************************************\
    *
    * This validity check is something a real app might actually like to do.
    *   We manupulated the SD, so before we write it back out to the file
    *   system, a check is worth considering
    *
    \************************************************************************/

    if (!IsValidSecurityDescriptor(psdFileSD))
    { PERR("IsValidSecurityDescriptor said bad SD");
      return(FALSE);
    }

    /************************************************************************\
    *
    * Modify the SD on the hard disk
    *
    \************************************************************************/

    if (!SetFileSecurity
           (lpszFullName,
            (SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
            psdFileSD))
    { PERR("SetFileSecurity");
      return(FALSE);
    }

    printf(" - edited ACL");
  }

  return(TRUE);

}

/****************************************************************************\
*
* FUNCTION: GetProcessSid
*
\****************************************************************************/

BOOL GetProcessSid(VOID)
{
  HANDLE               hProcess;
  PSECURITY_DESCRIPTOR psdProcessSD;
  PSID                 psidProcessOwnerSIDTemp;

  UCHAR                ucBuf       [SZ_REL_SD_BUF];
  DWORD                dwSDLength = SZ_REL_SD_BUF;
  DWORD                dwSDLengthNeeded;
  BOOL                 bOwnerDefaulted;

  hProcess = GetCurrentProcess();

  if (!hProcess)
  { PERR("GetCurrentProcess");
    return(FALSE);
  }

  psdProcessSD = (PSECURITY_DESCRIPTOR)ucBuf;

  if (!GetKernelObjectSecurity
         (hProcess,
          (SECURITY_INFORMATION)(OWNER_SECURITY_INFORMATION),
          psdProcessSD,
          dwSDLength,
          (LPDWORD)&dwSDLengthNeeded))
  { PERR("GetKernelObjectSecurity on current process handle");
    return(FALSE);
  }

  /**************************************************************************\
  *
  * This validity check is here for demonstration purposes.  It's not likely a
  *   real app would need to check the validity of this returned SD.  The
  *   validity check APIs are more intended to check validity after app code
  *   has manipulated the structure and is about to hand it back to the system
  *
  \**************************************************************************/

  if (!IsValidSecurityDescriptor(psdProcessSD))
  { PERR("IsValidSecurityDescriptor said bad SD");
    return(FALSE);
  }

  if (!GetSecurityDescriptorOwner
         (psdProcessSD,
          (PSID *)&psidProcessOwnerSIDTemp,
          (LPBOOL)&bOwnerDefaulted))
  { PERR("GetSecurityDescriptorOwner of current process");
    return(FALSE);
  }

  /**************************************************************************\
  *
  * This validity check is here for demonstration pruposes.  It's not likely a
  *   real app would need to check the validity of this returned SID.  The
  *   validity check APIs are more intended to check validity after app code
  *   has manipulated the structure and is about to hand it back to the system
  *
  \**************************************************************************/

  if (!IsValidSid(psidProcessOwnerSIDTemp))
  { PERR("IsValidSid said bad process SID!");
    return(FALSE);
  }

  /**************************************************************************\
  *
  * On the other hand, we are about to call GetLengthSid on the returned SID,
  *   and calling GetLengthSid with an invalid SID is a bad idea, since then
  *   GetLengthSid's result is undefined, and an undefined result is hard to
  *   handle cleanly.  So, even in a real app, the above check on SID validity
  *   is a good idea to ensure the result GetLengthSid returns is valid
  *
  * It should be clear that the reason why the CopySid below is needed is that
  *   in the current routine the SID of the current process is on the stack
  *   (in the SD structure), so we have to copy the SID to static storage
  *   before the current routine returns
  *
  \**************************************************************************/

  { DWORD dwSIDLengthNeeded;

    dwSIDLengthNeeded = GetLengthSid(psidProcessOwnerSIDTemp);

    psidProcessOwnerSID = malloc(dwSIDLengthNeeded);

    if (NULL == psidProcessOwnerSID)
      PERR("GetProcessSid - ran out of heap space");

    if (!CopySid(dwSIDLengthNeeded,
                 psidProcessOwnerSID,
                 psidProcessOwnerSIDTemp))
    { PERR("CopySid");
      return(FALSE);
    }
  }

  /**************************************************************************\
  *
  * This validity check is here for demonstration pruposes only (see above).
  *
  \**************************************************************************/

  if (!IsValidSid(psidProcessOwnerSID))
  { PERR("IsValidSid said bad process SID!");
    return(FALSE);
  }

  /**************************************************************************\
  *
  * Now ensure that two privileges are enabled in the access token of the
  *   current process
  *
  \**************************************************************************/

  { HANDLE           hAccessToken;
    LUID             luidPrivilegeLUID;
    TOKEN_PRIVILEGES tpTokenPrivilege;

    if (!OpenProcessToken(hProcess,
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                          &hAccessToken))
    { PERR("OpenProcessToken");
      return(FALSE);
    }

    /************************************************************************\
    *
    * Get LUID of SeTakeOwnershipPrivilege privilege
    *
    \************************************************************************/

    if (!LookupPrivilegeValue(NULL,
                              "SeTakeOwnershipPrivilege",
                              &luidPrivilegeLUID))
    { PERR("LookupPrivilegeValue");
      printf("\nThe above error means you need to use User Manager (menu item");
      printf("\n  Policies\\UserRights) to turn on the 'Take ownership of...' ");
      printf("\n  privilege, log off, log back on");
      return(FALSE);
    }

    /************************************************************************\
    *
    * Enable the SeTakeOwnershipPrivilege privilege using the LUID just
    *   obtained
    *
    \************************************************************************/

    tpTokenPrivilege.PrivilegeCount = 1;
    tpTokenPrivilege.Privileges[0].Luid = luidPrivilegeLUID;
    tpTokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    AdjustTokenPrivileges (hAccessToken,
                           FALSE,  // Do not disable all
                           &tpTokenPrivilege,
                           sizeof(TOKEN_PRIVILEGES),
                           NULL,   // Ignore previous info
                           NULL);  // Ignore previous info

    if ( GetLastError() != NO_ERROR )
    { PERR("AdjustTokenPrivileges");
      return(FALSE);
    }

    /************************************************************************\
    *
    * Get LUID of SeSecurityPrivilege privilege
    *
    \************************************************************************/

    if (!LookupPrivilegeValue(NULL,
                              "SeSecurityPrivilege",
                              &luidPrivilegeLUID))
    { PERR("LookupPrivilegeValue");
      printf("\nThe above error means you need to log on as an Administrator");
      return(FALSE);
    }

    /************************************************************************\
    *
    * Enable the SeSecurityPrivilege privilege using the LUID just
    *   obtained
    *
    \************************************************************************/

    tpTokenPrivilege.PrivilegeCount = 1;
    tpTokenPrivilege.Privileges[0].Luid = luidPrivilegeLUID;
    tpTokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    AdjustTokenPrivileges (hAccessToken,
                           FALSE,  // Do not disable all
                           &tpTokenPrivilege,
                           sizeof(TOKEN_PRIVILEGES),
                           NULL,   // Ignore previous info
                           NULL);  // Ignore previous info

    if ( GetLastError() != NO_ERROR )
    { PERR("AdjustTokenPrivileges");
      return(FALSE);
    }

  }

  return(TRUE);

}

/****************************************************************************\
*
* FUNCTION: CrackArgs
*
\****************************************************************************/

BOOL CrackArgs(UINT argc, char *argv[])
{
  char *p;

  /**************************************************************************\
  *
  * There must be three arguments
  *
  \**************************************************************************/

  if (argc != 4)
  { DisplayHelp();
    return(FALSE);
  }

  p=argv[1];

  /**************************************************************************\
  *
  * The switch argument must be 2-5 chars long
  *
  \**************************************************************************/

  if ((strlen(p) < 2) || (strlen(p) > 5))
  { DisplayHelp();
    return(FALSE);
  }

  /**************************************************************************\
  *
  * The first char in the switch argument must be /
  *
  \**************************************************************************/

  if ('/' != *p)
  { DisplayHelp();
    return(FALSE);
  }

  /**************************************************************************\
  *
  * Chars 2-5 of the switch argument must be O or A or R or C
  *
  \**************************************************************************/

  for (p=p+1; *p; p++)
    switch (*p)
    { case 'o':
      case 'O':
        bTakeOwnership = TRUE;
        break;
      case 'a':
      case 'A':
        bEditACLs      = TRUE;
        break;
      case 'r':
      case 'R':
        bRecurse       = TRUE;
        break;
      case 'c':
      case 'C':
        bJustCount     = TRUE;
        break;
      default :
        DisplayHelp();
        return(FALSE);
    }

  /**************************************************************************\
  *
  * Have to say one of O or A
  *
  \**************************************************************************/

  if (!(bTakeOwnership || bEditACLs))
  { DisplayHelp();
    return(FALSE);
  }

  return(TRUE);
}

/****************************************************************************\
*
* FUNCTION: DisplayHelp
*
\****************************************************************************/

VOID DisplayHelp(VOID)
{
  printf("\nTo run type SIDCLEAN and 3 parameters.  Syntax:");
  printf("\n  SIDCLEAN /roah dirspec filepattern");
  printf("\n           /r    Recursively process subdirectories");
  printf("\n           /o    For any files matching filepattern: Take ownership if");
  printf("\n                   file currently owned by any deleted SID");
  printf("\n           /a    For any files matching filepattern: Edit ACL, deleting");
  printf("\n                   ACEs associated with any deleted SID");
  printf("\n           /c    Overrides /o and /a, causes counts of /a or /o actions that");
  printf("\n                   would take place if /c not used.  Counts always displayed");
  printf("\n           /h    Override other switch values, just display this message\n");
  printf("\n                 . and .. syntax allowed in dirspec");
  printf("\n                 * and ? wildcards allowed in filepattern");
  printf("\n                 Switch letters can be in any order, upper or lower case");
  printf("\nExamples:");
  printf("\n  SIDCLEAN /o  .  *.*  Take ownership of all files (but not subdirs) in ");
  printf("\n                         current dir that are owned by any deleted SID");
  printf("\n  SIDCLEAN /a  .  *.*  For any file in current dir (but not subdirs), delete");
  printf("\n                         any ACL info that is associated with any deleted SID");
  printf("\n  SIDCLEAN /ro .  *.*  Same as first  example, but also recursively process");
  printf("\n                         subdirectories");
  printf("\n  SIDCLEAN /ar .  *.*  Same as second example, but also recursively process");
  printf("\n                         subdirectories");
  printf("\n  SIDCLEAN /O  \\  *.*  Same as first  example, but process files in root");
  printf("\n                         of current drive");
  printf("\n  SIDCLEAN /oC .. *.*  Same as first  example, but looks at files in dir");

printf("\n                         containing current dir, processes nothing, just counts");
  printf("\n  SIDCLEAN /A d:\\ *.*  Same as second example, but process files in root");
  printf("\n                         of D: drive");
  printf("\n  SIDCLEAN             Displays this message");
  printf("\n  SIDCLEAN /h          Displays this message (so do ? -? /? -h -H /H)\n");
  printf("\nThis utility must be run while logged on as Administrator\n");

  return;
}


README.TXT

SockAuth

This sample is distantly related to the httpauth sample which
demonstrates the use of the SSPI package to implement the client side
of NTLM authentication when interacting with IIS.  However, this sample
shows both the client and server conversations with the security
provider when just a socket connection is used.

The server side, in this sample, authenticates the client and provides
evidence of this by reporting the client's user name back to the client
via the socket connection.

KerbAuth Directory

This directory contains a modified version of security.c file, which
enables the sample to use Kerberos Security Package to obtain security
services.
Note the two changes:
    *  The package name is "kerberos"
    *  The TOKEN_SOURCE needs to be a valid principal name of the form
        "Domain\User"

NegoAuth Directory

This directory contains a modified version of security.c file, which
enables the sample to use Negotiate Security Package (SPNEGO) to obtain
security services.
Note the two changes:
    *   The package name is "negotiate"
    *   The TOKEN_SOURCE needs to be a valid principal name of the form
        "Domain\User"

Please copy the appropriate security.c to try out the sample using different
security packages.


COLLECT.C

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    collect.c

Abstract:

    Implements a very simple collection for mapping DWORD key values
to a blob of data.

Revision History:

--*/

#include <windows.h>
#include <stdlib.h>
#include "collect.h"

typedef struct _node_tag  {
DWORD dwKey;
PVOID *pData;
struct _node_tag *pNext;
} NODE, *PNODE;

static NODE Head = {(DWORD)-1, NULL, NULL};

BOOL GetEntry (DWORD dwKey, PVOID *ppData)
{
PNODE pCurrent;

pCurrent = Head.pNext;
while (NULL != pCurrent) {
if (dwKey == pCurrent->dwKey)  {
*ppData = pCurrent->pData;
return(TRUE);
}
pCurrent = pCurrent->pNext;
}

return(FALSE);
}

BOOL AddEntry (DWORD dwKey, PVOID pData)
{
PNODE pTemp;

pTemp = (PNODE) malloc (sizeof (NODE));
if (NULL == pTemp)  {
return(FALSE);
}

pTemp->dwKey = dwKey;
pTemp->pData = pData;
pTemp->pNext = Head.pNext;
Head.pNext = pTemp;

return(TRUE);
}

BOOL DeleteEntry (DWORD dwKey, PVOID *ppData)
{
PNODE pCurrent, pTemp;

pTemp = &Head;
pCurrent = Head.pNext;

while (NULL != pCurrent) {
if (dwKey == pCurrent->dwKey)  {
pTemp->pNext = pCurrent->pNext;
*ppData = pCurrent->pData;
free (pCurrent);
return(TRUE);
}
else {
pTemp = pCurrent;
pCurrent = pCurrent->pNext;
}
}

return(FALSE);
}


COLLECT.H

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    collect.h

Abstract:

    Functions for a simple collection

Revision History:

--*/

BOOL GetEntry (DWORD dwKey, PVOID *ppData);
BOOL AddEntry (DWORD dwKey, PVOID pData);
BOOL DeleteEntry (DWORD dwKey, PVOID *ppData);


COMM.C

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    comm.c

Abstract:

    Implements a set of common operations for socket communication

Revision History:

--*/

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include "comm.h"

BOOL InitWinsock ()
{
int nRes;
WSADATA wsaData;
WORD wVerRequested = 0x0101; // ver 1.1

// Init the sockets interface
nRes = WSAStartup (wVerRequested, &wsaData);
if (nRes)  {
fprintf (stderr, "Couldn't init winsock: %d\n", nRes);
return(FALSE);
}

return(TRUE);
}

BOOL TermWinsock ()
{
if (SOCKET_ERROR == WSACleanup ())
return(FALSE);
else
return(TRUE);
}

BOOL SendMsg (SOCKET s, PBYTE pBuf, DWORD cbBuf)
/*++

 Routine Description:

    Sends a message over the socket by first sending a DWORD that
represents the size of the message followed by the message itself.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
if (0 == cbBuf)
return(TRUE);

// send the size of the message
//
if (!SendBytes (s, (PBYTE)&cbBuf, sizeof (cbBuf)))
return(FALSE);

// send the body of the message
//
if (!SendBytes (s, pBuf, cbBuf))
return(FALSE);

return(TRUE);
}

BOOL ReceiveMsg (SOCKET s, PBYTE pBuf, DWORD cbBuf, DWORD *pcbRead)
/*++

 Routine Description:

    Receives a message over the socket.  The first DWORD in the message
will be the message size.  The remainder of the bytes will be the
actual message.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
DWORD cbRead;
DWORD cbData;

// find out how much data is in the message
//
if (!ReceiveBytes (s, (PBYTE)&cbData, sizeof (cbData), &cbRead))
return(FALSE);

if (sizeof (cbData) != cbRead)
return(FALSE);

// Read the full message
//
if (!ReceiveBytes (s, pBuf, cbData, &cbRead))
return(FALSE);

if (cbRead != cbData)
return(FALSE);

*pcbRead = cbRead;

return(TRUE);
}

BOOL SendBytes (SOCKET s, PBYTE pBuf, DWORD cbBuf)
{
PBYTE pTemp = pBuf;
int cbSent, cbRemaining = cbBuf;

if (0 == cbBuf)
return(TRUE);

while (cbRemaining) {
cbSent = send (s, pTemp, cbRemaining, 0);
if (SOCKET_ERROR == cbSent) {
fprintf (stderr, "send failed: %u\n", GetLastError ());
return FALSE;
}

pTemp += cbSent;
cbRemaining -= cbSent;
}

return TRUE;
}

BOOL ReceiveBytes (SOCKET s, PBYTE pBuf, DWORD cbBuf, DWORD *pcbRead)
{
PBYTE pTemp = pBuf;
int cbRead, cbRemaining = cbBuf;

while (cbRemaining) {
cbRead = recv (s, pTemp, cbRemaining, 0);
if (0 == cbRead)
break;

if (SOCKET_ERROR == cbRead) {
fprintf (stderr, "recv failed: %u\n", GetLastError ());
return FALSE;
}

cbRemaining -= cbRead;
pTemp += cbRead;
}

*pcbRead = cbBuf - cbRemaining;

return TRUE;
}


COMM.H

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    comm.h

Abstract:

    Some common functions for sockets

Revision History:

--*/

BOOL InitWinsock ();
BOOL TermWinsock ();
BOOL SendMsg (SOCKET s, PBYTE pBuf, DWORD cbBuf);
BOOL ReceiveMsg (SOCKET s, PBYTE pBuf, DWORD cbBuf, DWORD *pcbRead);
BOOL SendBytes (SOCKET s, PBYTE pBuf, DWORD cbBuf);
BOOL ReceiveBytes (SOCKET s, PBYTE pBuf, DWORD cbBuf, DWORD *pcbRead);


CLIENT.C

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    client.c

Abstract:

    A command line app that establishes an authenticated connection
with a server.

Revision History:

--*/

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include "security.h"
#include "comm.h"

BOOL ConnectAuthSocket (LPCSTR szServer, SOCKET *s);
BOOL CloseAuthSocket (SOCKET s);
BOOL DoAuthentication (SOCKET s);

static PBYTE g_pInBuf = NULL;
static PBYTE g_pOutBuf = NULL;
static DWORD g_cbMaxMessage = 0;
static unsigned short g_usPort = 2000;

void main(int argc, char *argv[])
{
SOCKET s;
int cbRead;
char achData[BUFSIZ];

if (argc < 2)  {
fprintf (stderr, "usage: %s <server>\n", argv[0]);
fprintf (stderr, " example: %s winbase\n", argv[0]);
exit (EXIT_FAILURE);
}

// initialize
//
if (!InitWinsock ())
exit (EXIT_FAILURE);

if (!InitPackage (&g_cbMaxMessage))
exit (EXIT_FAILURE);

g_pInBuf = (PBYTE) malloc (g_cbMaxMessage);
g_pOutBuf = (PBYTE) malloc (g_cbMaxMessage);
if (NULL == g_pInBuf || NULL == g_pOutBuf)
exit (EXIT_FAILURE);

// connect to server
//
if (!ConnectAuthSocket (argv[1], &s))
exit (EXIT_FAILURE);

while (1) {
// read from the socket
//
if (!ReceiveBytes (s, achData, BUFSIZ, &cbRead))
break;

if (0 == cbRead)
break;

// display the results
//
printf ("%.*s", cbRead, achData);
}

printf ("\n");

// terminate
//
CloseAuthSocket (s);

TermPackage ();

TermWinsock ();

free (g_pInBuf);
free (g_pOutBuf);

exit (EXIT_SUCCESS);
}

BOOL ConnectAuthSocket (LPCSTR szServer, SOCKET *s)
/*++

 Routine Description:

    Establishes an authenticated socket connection with a server and
initializes any needed security package resources.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SOCKET sockServer;
unsigned long ulAddress;
struct hostent *pHost;
SOCKADDR_IN sin;
DWORD dwRes;

// lookup the address for the server name
//
ulAddress = inet_addr (szServer);
if (INADDR_NONE == ulAddress) {
pHost = gethostbyname (szServer);
if (NULL == pHost) {
dwRes = GetLastError ();
fprintf (stderr, "Unable to resolve host name: %u\n", dwRes);
return(FALSE);
}

memcpy((char FAR *)&ulAddress, pHost->h_addr, pHost->h_length);
}

// create the socket
//
sockServer = socket (PF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockServer) {
fprintf (stderr, "Unable to create socket: %u\n", GetLastError ());
return(FALSE);
}

sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ulAddress;
sin.sin_port = htons (g_usPort);

// connect to remote endpoint
//
if (connect (sockServer, (LPSOCKADDR) &sin, sizeof (sin))) {
fprintf (stderr, "connect failed: %u\n", GetLastError ());
closesocket (sockServer);
return(FALSE);
}

// Make this an authenticated connection
//
if (!InitSession (sockServer)) {
closesocket (sockServer);
return(FALSE);
}

if (!DoAuthentication (sockServer)) {
closesocket (sockServer);
return(FALSE);
}

*s = sockServer;

return(TRUE);
}

BOOL CloseAuthSocket (SOCKET s)
/*++

 Routine Description:

    Closes a socket and releases security resources associated with
the socket

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
TermSession (s);
shutdown (s, 2);
closesocket (s);
return(TRUE);
}

BOOL DoAuthentication (SOCKET s)
/*++

 Routine Description:

    Manges the authentication conversation with the server via the
    supplied socket handle.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
BOOL done = FALSE;
DWORD cbOut, cbIn;

cbOut = g_cbMaxMessage;
if (!GenClientContext (s, NULL, 0, g_pOutBuf, &cbOut, &done))
return(FALSE);

if (!SendMsg (s, g_pOutBuf, cbOut))
return(FALSE);

while (!done) {
if (!ReceiveMsg (s, g_pInBuf, g_cbMaxMessage, &cbIn))
return(FALSE);

cbOut = g_cbMaxMessage;
if (!GenClientContext (s, g_pInBuf, cbIn, g_pOutBuf, &cbOut, &done))
return(FALSE);

if (!SendMsg (s, g_pOutBuf, cbOut))
return(FALSE);
}

return(TRUE);
}


SECURITY.C

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    security.c

Abstract:

    Handles communication with the SSP package.

Revision History:

--*/

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#define SECURITY_WIN32
#include "sspi.h"
#include "issperr.h"
#include "security.h"
#include "collect.h"

static HINSTANCE g_hLib;
static DWORD g_cbMaxToken;
static PSecurityFunctionTable g_pFuncs;

// structure storing the state of the authentication sequence
//
typedef struct _AUTH_SEQ {
    BOOL _fNewConversation;
    CredHandle _hcred;
    BOOL _fHaveCredHandle;
    BOOL _fHaveCtxtHandle;
    struct _SecHandle  _hctxt;
} AUTH_SEQ, *PAUTH_SEQ;

#define SEC_SUCCESS(Status) ((Status) >= 0)

#define PACKAGE_NAME"NTLM"
#define NT_DLL_NAME"security.dll"

// Target name for the security package
//
#define TOKEN_SOURCE_NAME       "AuthSamp"

BOOL InitPackage (DWORD *pcbMaxMessage)
/*++

 Routine Description:

    Finds, loads and initializes the security package

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
FARPROC pInit;
SECURITY_STATUS ss;
PSecPkgInfo pkgInfo;

// load and initialize the ntlm ssp
//
g_hLib = LoadLibrary (NT_DLL_NAME);
if (NULL == g_hLib)  {
fprintf (stderr, "Couldn't load dll: %u\n", GetLastError ());
return(FALSE);
}

pInit = GetProcAddress (g_hLib, SECURITY_ENTRYPOINT);
if (NULL == pInit)  {
fprintf (stderr, "Couldn't get sec init routine: %u\n", GetLastError ());
return(FALSE);
}

g_pFuncs = (PSecurityFunctionTable) pInit ();
if (NULL == g_pFuncs)  {
fprintf (stderr, "Couldn't init package\n");
return(FALSE);
}

// Query for the package we're interested in
//
ss = g_pFuncs->QuerySecurityPackageInfo (PACKAGE_NAME, &pkgInfo);
if (!SEC_SUCCESS(ss))  {
fprintf (stderr, "Couldn't query package info for %s, error %u\n",
PACKAGE_NAME, ss);
return(FALSE);
}

g_cbMaxToken = pkgInfo->cbMaxToken;

g_pFuncs->FreeContextBuffer (pkgInfo);

*pcbMaxMessage = g_cbMaxToken;

return TRUE;
}

BOOL TermPackage ()
{
FreeLibrary (g_hLib);

return(TRUE);
}

BOOL GenClientContext (
DWORD dwKey,
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone)
/*++

 Routine Description:

    Optionally takes an input buffer coming from the server and returns
a buffer of information to send back to the server.  Also returns
an indication of whether or not the context is complete.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUSss;
TimeStampLifetime;
SecBufferDescOutBuffDesc;
SecBufferOutSecBuff;
SecBufferDescInBuffDesc;
SecBufferInSecBuff;
ULONGContextAttributes;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

if (pAS->_fNewConversation)  {
ss = g_pFuncs->AcquireCredentialsHandle (
NULL,// principal
PACKAGE_NAME,
SECPKG_CRED_OUTBOUND,
NULL,// LOGON id
NULL,// auth data
NULL,// get key fn
NULL,// get key arg
&pAS->_hcred,
&Lifetime
);
if (SEC_SUCCESS (ss))
pAS->_fHaveCredHandle = TRUE;
else {
fprintf (stderr, "AcquireCreds failed: %u\n", ss);
return(FALSE);
}
}

// prepare output buffer
//
OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;

OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = pOut;

// prepare input buffer
//
if (!pAS->_fNewConversation)  {
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;

InSecBuff.cbBuffer = cbIn;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.pvBuffer = pIn;
}

ss = g_pFuncs->InitializeSecurityContext (
&pAS->_hcred,
pAS->_fNewConversation ? NULL : &pAS->_hctxt,
TOKEN_SOURCE_NAME,
0,// context requirements
0,// reserved1
SECURITY_NATIVE_DREP,
pAS->_fNewConversation ? NULL : &InBuffDesc,
0,// reserved2
&pAS->_hctxt,
&OutBuffDesc,
&ContextAttributes,
&Lifetime
);
if (!SEC_SUCCESS (ss))  {
fprintf (stderr, "init context failed: %u\n", ss);
return FALSE;
}

pAS->_fHaveCtxtHandle = TRUE;

// Complete token -- if applicable
//
if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))  {
if (g_pFuncs->CompleteAuthToken) {
ss = g_pFuncs->CompleteAuthToken (&pAS->_hctxt, &OutBuffDesc);
if (!SEC_SUCCESS(ss))  {
fprintf (stderr, "complete failed: %u\n", ss);
return FALSE;
}
}
else {
fprintf (stderr, "Complete not supported.\n");
return FALSE;
}
}

*pcbOut = OutSecBuff.cbBuffer;

if (pAS->_fNewConversation)
pAS->_fNewConversation = FALSE;

*pfDone = !((SEC_I_CONTINUE_NEEDED == ss) ||
(SEC_I_COMPLETE_AND_CONTINUE == ss));

return TRUE;
}

BOOL GenServerContext (
DWORD dwKey,
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone)
/*++

 Routine Description:

    Takes an input buffer coming from the client and returns a buffer
to be sent to the client.  Also returns an indication of whether or
not the context is complete.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUSss;
TimeStampLifetime;
SecBufferDescOutBuffDesc;
SecBufferOutSecBuff;
SecBufferDescInBuffDesc;
SecBufferInSecBuff;
ULONGContextAttributes;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

if (pAS->_fNewConversation)  {
ss = g_pFuncs->AcquireCredentialsHandle (
NULL,// principal
PACKAGE_NAME,
SECPKG_CRED_INBOUND,
NULL,// LOGON id
NULL,// auth data
NULL,// get key fn
NULL,// get key arg
&pAS->_hcred,
&Lifetime
);
if (SEC_SUCCESS (ss))
pAS->_fHaveCredHandle = TRUE;
else {
fprintf (stderr, "AcquireCreds failed: %u\n", ss);
return(FALSE);
}
}

// prepare output buffer
//
OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;

OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = pOut;

// prepare input buffer
//
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;

InSecBuff.cbBuffer = cbIn;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.pvBuffer = pIn;

ss = g_pFuncs->AcceptSecurityContext (
&pAS->_hcred,
pAS->_fNewConversation ? NULL : &pAS->_hctxt,
&InBuffDesc,
0,// context requirements
SECURITY_NATIVE_DREP,
&pAS->_hctxt,
&OutBuffDesc,
&ContextAttributes,
&Lifetime
);
if (!SEC_SUCCESS (ss))  {
fprintf (stderr, "init context failed: %u\n", ss);
return FALSE;
}

pAS->_fHaveCtxtHandle = TRUE;

// Complete token -- if applicable
//
if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))  {
if (g_pFuncs->CompleteAuthToken) {
ss = g_pFuncs->CompleteAuthToken (&pAS->_hctxt, &OutBuffDesc);
if (!SEC_SUCCESS(ss))  {
fprintf (stderr, "complete failed: %u\n", ss);
return FALSE;
}
}
else {
fprintf (stderr, "Complete not supported.\n");
return FALSE;
}
}

*pcbOut = OutSecBuff.cbBuffer;

if (pAS->_fNewConversation)
pAS->_fNewConversation = FALSE;

*pfDone = !((SEC_I_CONTINUE_NEEDED == ss) ||
(SEC_I_COMPLETE_AND_CONTINUE == ss));

return TRUE;
}

BOOL ImpersonateContext (DWORD dwKey)
/*++

 Routine Description:

    Impersonates the client whose context is associated with the
supplied key.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUSss;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

ss = g_pFuncs->ImpersonateSecurityContext (&pAS->_hctxt);
if (!SEC_SUCCESS(ss)) {
fprintf (stderr, "Impersonate failed: %u\n", ss);
return(FALSE);
}

return(TRUE);
}

BOOL RevertContext (DWORD dwKey)
/*++

 Routine Description:

    Reverts to the original server context.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUS ss;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

ss = g_pFuncs->RevertSecurityContext (&pAS->_hctxt);
if (!SEC_SUCCESS(ss)) {
fprintf (stderr, "Revert failed: %u\n", ss);
return(FALSE);
}

return(TRUE);
}

BOOL InitSession (DWORD dwKey)
/*++

 Routine Description:

    Initializes the context associated with a key and adds it to the
collection.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
PAUTH_SEQ pAS;

pAS = (PAUTH_SEQ) malloc (sizeof (AUTH_SEQ));
if (NULL == pAS)
return(FALSE);

pAS->_fNewConversation = TRUE;
pAS->_fHaveCredHandle = FALSE;
pAS->_fHaveCtxtHandle = FALSE;

if (!AddEntry (dwKey, (PVOID)pAS))  {
free (pAS);
return(FALSE);
}

return(TRUE);
}

BOOL TermSession (DWORD dwKey)
/*++

 Routine Description:

    Releases the resources associated with a key and removes it from
the collection.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
PAUTH_SEQ pAS;

if (!DeleteEntry (dwKey, (LPVOID*)&pAS))
return(FALSE);

if (pAS->_fHaveCtxtHandle)
g_pFuncs->DeleteSecurityContext (&pAS->_hctxt);

if (pAS->_fHaveCredHandle)
g_pFuncs->FreeCredentialHandle (&pAS->_hcred);

free (pAS);

return(TRUE);
}


SECURITY.H

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    security.h

Abstract:

    Functions to handle communication with the security package

Revision History:

--*/

BOOL InitPackage (DWORD *pcbMaxMessage);
BOOL TermPackage ();
BOOL InitSession (DWORD dwKey);
BOOL TermSession (DWORD dwKey);
BOOL GenClientContext (
DWORD dwKey,
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone
);
BOOL GenServerContext (
DWORD dwKey,
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone
);
BOOL ImpersonateContext (DWORD dwKey);
BOOL RevertContext (DWORD dwKey);


SERVER.C

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    server.c

Abstract:

    A command line app that establishes an authenticated connection
with a client.

Revision History:

--*/

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include "security.h"
#include "comm.h"

BOOL AcceptAuthSocket (SOCKET *s);
BOOL CloseAuthSocket (SOCKET s);
BOOL DoAuthentication (SOCKET s);

static PBYTE g_pInBuf = NULL;
static PBYTE g_pOutBuf = NULL;
static DWORD g_cbMaxMessage = 0;
static unsigned short g_usPort = 2000;

void main ()
{
char szUser[80];
DWORD cbUser = 80;
SOCKET s;

// initialize
//
if (!InitWinsock ())
exit (EXIT_FAILURE);

if (!InitPackage (&g_cbMaxMessage))
exit (EXIT_FAILURE);

g_pInBuf = (PBYTE) malloc (g_cbMaxMessage);
g_pOutBuf = (PBYTE) malloc (g_cbMaxMessage);
if (NULL == g_pInBuf || NULL == g_pOutBuf)
exit (EXIT_FAILURE);

// Make an authenticated connection with client
//
if (!AcceptAuthSocket (&s))
exit (EXIT_FAILURE);

// impersonate the client
//
if (!ImpersonateContext (s))
exit (EXIT_FAILURE);

// get the user name
//
if (!GetUserName (szUser, &cbUser))
exit (EXIT_FAILURE);

// revert to self
//
if (!RevertContext (s))
exit (EXIT_FAILURE);

// send user name to client
//
if (!SendBytes (s, szUser, cbUser))
exit (EXIT_FAILURE);

// terminate and cleanup
//
CloseAuthSocket (s);

TermPackage ();

TermWinsock ();

free (g_pInBuf);
free (g_pOutBuf);

exit (EXIT_SUCCESS);
}

BOOL AcceptAuthSocket (SOCKET *s)
/*++

 Routine Description:

    Establishes an authenticated socket connection with a client and
initializes any needed security package resources.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SOCKET sockListen;
SOCKET sockClient;
SOCKADDR_IN sin;
int nRes;

// create listening socket
//
sockListen = socket (PF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockListen)  {
fprintf (stderr, "Failed to create socket: %u\n", GetLastError ());
return(FALSE);
}

// bind to local port
//
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(g_usPort);
nRes = bind (sockListen, (LPSOCKADDR) &sin, sizeof (sin));
if (SOCKET_ERROR == nRes)  {
fprintf (stderr, "bind failed: %u\n", GetLastError ());
return(FALSE);
}

// listen for client
//
nRes = listen (sockListen, 1);
if (SOCKET_ERROR == nRes)  {
fprintf (stderr, "listen failed: %u\n", GetLastError ());
return(FALSE);
}

// accept client
//
sockClient = accept (sockListen, NULL, NULL);
if (INVALID_SOCKET == sockClient)  {
fprintf (stderr, "accept failed: %u\n", GetLastError ());
return(FALSE);
}

closesocket (sockListen);

if (!InitSession (sockClient))
return(FALSE);

if (!DoAuthentication (sockClient))
return(FALSE);

// return socket
//
*s = sockClient;

return(TRUE);
}

BOOL CloseAuthSocket (SOCKET s)
/*++

 Routine Description:

    Closes a socket and releases security resources associated with
the socket

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
TermSession (s);
shutdown (s, 2);
closesocket (s);
return(TRUE);
}

BOOL DoAuthentication (SOCKET s)
/*++

 Routine Description:

    Manges the authentication conversation with the client via the
    supplied socket handle.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
DWORD cbIn, cbOut;
BOOL done = FALSE;

do {
if (!ReceiveMsg (s, g_pInBuf, g_cbMaxMessage, &cbIn))
return(FALSE);

cbOut = g_cbMaxMessage;
if (!GenServerContext (s, g_pInBuf, cbIn, g_pOutBuf, &cbOut, &done))
return(FALSE);

if (!SendMsg (s, g_pOutBuf, cbOut))
return(FALSE);
}
while(!done);

return(TRUE);
}


SECURITY.C

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    security.c

Abstract:

    Handles communication with the SSP package.

Revision History:

--*/

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#define SECURITY_WIN32
#include "sspi.h"
#include "issperr.h"
#include "security.h"
#include "collect.h"

static HINSTANCE g_hLib;
static DWORD g_cbMaxToken;
static PSecurityFunctionTable g_pFuncs;

// structure storing the state of the authentication sequence
//
typedef struct _AUTH_SEQ {
    BOOL _fNewConversation;
    CredHandle _hcred;
    BOOL _fHaveCredHandle;
    BOOL _fHaveCtxtHandle;
    struct _SecHandle  _hctxt;
} AUTH_SEQ, *PAUTH_SEQ;

#define SEC_SUCCESS(Status) ((Status) >= 0)

#define PACKAGE_NAME"kerberos"
#define NT_DLL_NAME"security.dll"

// Target name for the security package
// De-comment after filling appropriate principal name here.
//
// #define TOKEN_SOURCE_NAME       "<Domain>\\<User>"

BOOL InitPackage (DWORD *pcbMaxMessage)
/*++

 Routine Description:

    Finds, loads and initializes the security package

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
FARPROC pInit;
SECURITY_STATUS ss;
PSecPkgInfo pkgInfo;

// load and initialize the ntlm ssp
//
g_hLib = LoadLibrary (NT_DLL_NAME);
if (NULL == g_hLib)  {
fprintf (stderr, "Couldn't load dll: %u\n", GetLastError ());
return(FALSE);
}

pInit = GetProcAddress (g_hLib, SECURITY_ENTRYPOINT);
if (NULL == pInit)  {
fprintf (stderr, "Couldn't get sec init routine: %u\n", GetLastError ());
return(FALSE);
}

g_pFuncs = (PSecurityFunctionTable) pInit ();
if (NULL == g_pFuncs)  {
fprintf (stderr, "Couldn't init package\n");
return(FALSE);
}

// Query for the package we're interested in
//
ss = g_pFuncs->QuerySecurityPackageInfo (PACKAGE_NAME, &pkgInfo);
if (!SEC_SUCCESS(ss))  {
fprintf (stderr, "Couldn't query package info for %s, error %u\n",
PACKAGE_NAME, ss);
return(FALSE);
}

g_cbMaxToken = pkgInfo->cbMaxToken;

g_pFuncs->FreeContextBuffer (pkgInfo);

*pcbMaxMessage = g_cbMaxToken;

return TRUE;
}

BOOL TermPackage ()
{
FreeLibrary (g_hLib);

return(TRUE);
}

BOOL GenClientContext (
DWORD dwKey,
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone)
/*++

 Routine Description:

    Optionally takes an input buffer coming from the server and returns
a buffer of information to send back to the server.  Also returns
an indication of whether or not the context is complete.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUSss;
TimeStampLifetime;
SecBufferDescOutBuffDesc;
SecBufferOutSecBuff;
SecBufferDescInBuffDesc;
SecBufferInSecBuff;
ULONGContextAttributes;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

if (pAS->_fNewConversation)  {
ss = g_pFuncs->AcquireCredentialsHandle (
NULL,// principal
PACKAGE_NAME,
SECPKG_CRED_OUTBOUND,
NULL,// LOGON id
NULL,// auth data
NULL,// get key fn
NULL,// get key arg
&pAS->_hcred,
&Lifetime
);
if (SEC_SUCCESS (ss))
pAS->_fHaveCredHandle = TRUE;
else {
fprintf (stderr, "AcquireCreds failed: %u\n", ss);
return(FALSE);
}
}

// prepare output buffer
//
OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;

OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = pOut;

// prepare input buffer
//
if (!pAS->_fNewConversation)  {
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;

InSecBuff.cbBuffer = cbIn;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.pvBuffer = pIn;
}

ss = g_pFuncs->InitializeSecurityContext (
&pAS->_hcred,
pAS->_fNewConversation ? NULL : &pAS->_hctxt,
TOKEN_SOURCE_NAME,
0,// context requirements
0,// reserved1
SECURITY_NATIVE_DREP,
pAS->_fNewConversation ? NULL : &InBuffDesc,
0,// reserved2
&pAS->_hctxt,
&OutBuffDesc,
&ContextAttributes,
&Lifetime
);
if (!SEC_SUCCESS (ss))  {
fprintf (stderr, "init context failed: %u\n", ss);
return FALSE;
}

pAS->_fHaveCtxtHandle = TRUE;

// Complete token -- if applicable
//
if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))  {
if (g_pFuncs->CompleteAuthToken) {
ss = g_pFuncs->CompleteAuthToken (&pAS->_hctxt, &OutBuffDesc);
if (!SEC_SUCCESS(ss))  {
fprintf (stderr, "complete failed: %u\n", ss);
return FALSE;
}
}
else {
fprintf (stderr, "Complete not supported.\n");
return FALSE;
}
}

*pcbOut = OutSecBuff.cbBuffer;

if (pAS->_fNewConversation)
pAS->_fNewConversation = FALSE;

*pfDone = !((SEC_I_CONTINUE_NEEDED == ss) ||
(SEC_I_COMPLETE_AND_CONTINUE == ss));

return TRUE;
}

BOOL GenServerContext (
DWORD dwKey,
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone)
/*++

 Routine Description:

    Takes an input buffer coming from the client and returns a buffer
to be sent to the client.  Also returns an indication of whether or
not the context is complete.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUSss;
TimeStampLifetime;
SecBufferDescOutBuffDesc;
SecBufferOutSecBuff;
SecBufferDescInBuffDesc;
SecBufferInSecBuff;
ULONGContextAttributes;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

if (pAS->_fNewConversation)  {
ss = g_pFuncs->AcquireCredentialsHandle (
NULL,// principal
PACKAGE_NAME,
SECPKG_CRED_INBOUND,
NULL,// LOGON id
NULL,// auth data
NULL,// get key fn
NULL,// get key arg
&pAS->_hcred,
&Lifetime
);
if (SEC_SUCCESS (ss))
pAS->_fHaveCredHandle = TRUE;
else {
fprintf (stderr, "AcquireCreds failed: %u\n", ss);
return(FALSE);
}
}

// prepare output buffer
//
OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;

OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = pOut;

// prepare input buffer
//
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;

InSecBuff.cbBuffer = cbIn;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.pvBuffer = pIn;

ss = g_pFuncs->AcceptSecurityContext (
&pAS->_hcred,
pAS->_fNewConversation ? NULL : &pAS->_hctxt,
&InBuffDesc,
0,// context requirements
SECURITY_NATIVE_DREP,
&pAS->_hctxt,
&OutBuffDesc,
&ContextAttributes,
&Lifetime
);
if (!SEC_SUCCESS (ss))  {
fprintf (stderr, "init context failed: %u\n", ss);
return FALSE;
}

pAS->_fHaveCtxtHandle = TRUE;

// Complete token -- if applicable
//
if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))  {
if (g_pFuncs->CompleteAuthToken) {
ss = g_pFuncs->CompleteAuthToken (&pAS->_hctxt, &OutBuffDesc);
if (!SEC_SUCCESS(ss))  {
fprintf (stderr, "complete failed: %u\n", ss);
return FALSE;
}
}
else {
fprintf (stderr, "Complete not supported.\n");
return FALSE;
}
}

*pcbOut = OutSecBuff.cbBuffer;

if (pAS->_fNewConversation)
pAS->_fNewConversation = FALSE;

*pfDone = !((SEC_I_CONTINUE_NEEDED == ss) ||
(SEC_I_COMPLETE_AND_CONTINUE == ss));

return TRUE;
}

BOOL ImpersonateContext (DWORD dwKey)
/*++

 Routine Description:

    Impersonates the client whose context is associated with the
supplied key.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUSss;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

ss = g_pFuncs->ImpersonateSecurityContext (&pAS->_hctxt);
if (!SEC_SUCCESS(ss)) {
fprintf (stderr, "Impersonate failed: %u\n", ss);
return(FALSE);
}

return(TRUE);
}

BOOL RevertContext (DWORD dwKey)
/*++

 Routine Description:

    Reverts to the original server context.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUS ss;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

ss = g_pFuncs->RevertSecurityContext (&pAS->_hctxt);
if (!SEC_SUCCESS(ss)) {
fprintf (stderr, "Revert failed: %u\n", ss);
return(FALSE);
}

return(TRUE);
}

BOOL InitSession (DWORD dwKey)
/*++

 Routine Description:

    Initializes the context associated with a key and adds it to the
collection.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
PAUTH_SEQ pAS;

pAS = (PAUTH_SEQ) malloc (sizeof (AUTH_SEQ));
if (NULL == pAS)
return(FALSE);

pAS->_fNewConversation = TRUE;
pAS->_fHaveCredHandle = FALSE;
pAS->_fHaveCtxtHandle = FALSE;

if (!AddEntry (dwKey, (PVOID)pAS))  {
free (pAS);
return(FALSE);
}

return(TRUE);
}

BOOL TermSession (DWORD dwKey)
/*++

 Routine Description:

    Releases the resources associated with a key and removes it from
the collection.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
PAUTH_SEQ pAS;

if (!DeleteEntry (dwKey, (LPVOID*)&pAS))
return(FALSE);

if (pAS->_fHaveCtxtHandle)
g_pFuncs->DeleteSecurityContext (&pAS->_hctxt);

if (pAS->_fHaveCredHandle)
g_pFuncs->FreeCredentialHandle (&pAS->_hcred);

free (pAS);

return(TRUE);
}


SECURITY.C

/*++

Copyright 1996 - 1998 Microsoft Corporation

Module Name:

    security.c

Abstract:

    Handles communication with the SSP package.

Revision History:

--*/

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#define SECURITY_WIN32
#include "sspi.h"
#include "issperr.h"
#include "security.h"
#include "collect.h"

static HINSTANCE g_hLib;
static DWORD g_cbMaxToken;
static PSecurityFunctionTable g_pFuncs;

// structure storing the state of the authentication sequence
//
typedef struct _AUTH_SEQ {
    BOOL _fNewConversation;
    CredHandle _hcred;
    BOOL _fHaveCredHandle;
    BOOL _fHaveCtxtHandle;
    struct _SecHandle  _hctxt;
} AUTH_SEQ, *PAUTH_SEQ;

#define SEC_SUCCESS(Status) ((Status) >= 0)

#define PACKAGE_NAME"negotiate"
#define NT_DLL_NAME"security.dll"

// Target name for the security package
// De-comment after filling appropriate principal name, here.
//
// #define TOKEN_SOURCE_NAME       "<Domain>\\<User>"

BOOL InitPackage (DWORD *pcbMaxMessage)
/*++

 Routine Description:

    Finds, loads and initializes the security package

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
FARPROC pInit;
SECURITY_STATUS ss;
PSecPkgInfo pkgInfo;

// load and initialize the ntlm ssp
//
g_hLib = LoadLibrary (NT_DLL_NAME);
if (NULL == g_hLib)  {
fprintf (stderr, "Couldn't load dll: %u\n", GetLastError ());
return(FALSE);
}

pInit = GetProcAddress (g_hLib, SECURITY_ENTRYPOINT);
if (NULL == pInit)  {
fprintf (stderr, "Couldn't get sec init routine: %u\n", GetLastError ());
return(FALSE);
}

g_pFuncs = (PSecurityFunctionTable) pInit ();
if (NULL == g_pFuncs)  {
fprintf (stderr, "Couldn't init package\n");
return(FALSE);
}

// Query for the package we're interested in
//
ss = g_pFuncs->QuerySecurityPackageInfo (PACKAGE_NAME, &pkgInfo);
if (!SEC_SUCCESS(ss))  {
fprintf (stderr, "Couldn't query package info for %s, error %u\n",
PACKAGE_NAME, ss);
return(FALSE);
}

g_cbMaxToken = pkgInfo->cbMaxToken;

g_pFuncs->FreeContextBuffer (pkgInfo);

*pcbMaxMessage = g_cbMaxToken;

return TRUE;
}

BOOL TermPackage ()
{
FreeLibrary (g_hLib);

return(TRUE);
}

BOOL GenClientContext (
DWORD dwKey,
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone)
/*++

 Routine Description:

    Optionally takes an input buffer coming from the server and returns
a buffer of information to send back to the server.  Also returns
an indication of whether or not the context is complete.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUSss;
TimeStampLifetime;
SecBufferDescOutBuffDesc;
SecBufferOutSecBuff;
SecBufferDescInBuffDesc;
SecBufferInSecBuff;
ULONGContextAttributes;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

if (pAS->_fNewConversation)  {
ss = g_pFuncs->AcquireCredentialsHandle (
NULL,// principal
PACKAGE_NAME,
SECPKG_CRED_OUTBOUND,
NULL,// LOGON id
NULL,// auth data
NULL,// get key fn
NULL,// get key arg
&pAS->_hcred,
&Lifetime
);
if (SEC_SUCCESS (ss))
pAS->_fHaveCredHandle = TRUE;
else {
fprintf (stderr, "AcquireCreds failed: %u\n", ss);
return(FALSE);
}
}

// prepare output buffer
//
OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;

OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = pOut;

// prepare input buffer
//
if (!pAS->_fNewConversation)  {
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;

InSecBuff.cbBuffer = cbIn;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.pvBuffer = pIn;
}

ss = g_pFuncs->InitializeSecurityContext (
&pAS->_hcred,
pAS->_fNewConversation ? NULL : &pAS->_hctxt,
TOKEN_SOURCE_NAME,
0,// context requirements
0,// reserved1
SECURITY_NATIVE_DREP,
pAS->_fNewConversation ? NULL : &InBuffDesc,
0,// reserved2
&pAS->_hctxt,
&OutBuffDesc,
&ContextAttributes,
&Lifetime
);
if (!SEC_SUCCESS (ss))  {
fprintf (stderr, "init context failed: %u\n", ss);
return FALSE;
}

pAS->_fHaveCtxtHandle = TRUE;

// Complete token -- if applicable
//
if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))  {
if (g_pFuncs->CompleteAuthToken) {
ss = g_pFuncs->CompleteAuthToken (&pAS->_hctxt, &OutBuffDesc);
if (!SEC_SUCCESS(ss))  {
fprintf (stderr, "complete failed: %u\n", ss);
return FALSE;
}
}
else {
fprintf (stderr, "Complete not supported.\n");
return FALSE;
}
}

*pcbOut = OutSecBuff.cbBuffer;

if (pAS->_fNewConversation)
pAS->_fNewConversation = FALSE;

*pfDone = !((SEC_I_CONTINUE_NEEDED == ss) ||
(SEC_I_COMPLETE_AND_CONTINUE == ss));

return TRUE;
}

BOOL GenServerContext (
DWORD dwKey,
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone)
/*++

 Routine Description:

    Takes an input buffer coming from the client and returns a buffer
to be sent to the client.  Also returns an indication of whether or
not the context is complete.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUSss;
TimeStampLifetime;
SecBufferDescOutBuffDesc;
SecBufferOutSecBuff;
SecBufferDescInBuffDesc;
SecBufferInSecBuff;
ULONGContextAttributes;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

if (pAS->_fNewConversation)  {
ss = g_pFuncs->AcquireCredentialsHandle (
NULL,// principal
PACKAGE_NAME,
SECPKG_CRED_INBOUND,
NULL,// LOGON id
NULL,// auth data
NULL,// get key fn
NULL,// get key arg
&pAS->_hcred,
&Lifetime
);
if (SEC_SUCCESS (ss))
pAS->_fHaveCredHandle = TRUE;
else {
fprintf (stderr, "AcquireCreds failed: %u\n", ss);
return(FALSE);
}
}

// prepare output buffer
//
OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;

OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = pOut;

// prepare input buffer
//
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;

InSecBuff.cbBuffer = cbIn;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.pvBuffer = pIn;

ss = g_pFuncs->AcceptSecurityContext (
&pAS->_hcred,
pAS->_fNewConversation ? NULL : &pAS->_hctxt,
&InBuffDesc,
0,// context requirements
SECURITY_NATIVE_DREP,
&pAS->_hctxt,
&OutBuffDesc,
&ContextAttributes,
&Lifetime
);
if (!SEC_SUCCESS (ss))  {
fprintf (stderr, "init context failed: %u\n", ss);
return FALSE;
}

pAS->_fHaveCtxtHandle = TRUE;

// Complete token -- if applicable
//
if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))  {
if (g_pFuncs->CompleteAuthToken) {
ss = g_pFuncs->CompleteAuthToken (&pAS->_hctxt, &OutBuffDesc);
if (!SEC_SUCCESS(ss))  {
fprintf (stderr, "complete failed: %u\n", ss);
return FALSE;
}
}
else {
fprintf (stderr, "Complete not supported.\n");
return FALSE;
}
}

*pcbOut = OutSecBuff.cbBuffer;

if (pAS->_fNewConversation)
pAS->_fNewConversation = FALSE;

*pfDone = !((SEC_I_CONTINUE_NEEDED == ss) ||
(SEC_I_COMPLETE_AND_CONTINUE == ss));

return TRUE;
}

BOOL ImpersonateContext (DWORD dwKey)
/*++

 Routine Description:

    Impersonates the client whose context is associated with the
supplied key.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUSss;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

ss = g_pFuncs->ImpersonateSecurityContext (&pAS->_hctxt);
if (!SEC_SUCCESS(ss)) {
fprintf (stderr, "Impersonate failed: %u\n", ss);
return(FALSE);
}

return(TRUE);
}

BOOL RevertContext (DWORD dwKey)
/*++

 Routine Description:

    Reverts to the original server context.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
SECURITY_STATUS ss;
PAUTH_SEQpAS;

// Lookup pAS based on Key
//
if (!GetEntry (dwKey, (PVOID*) &pAS))
return(FALSE);

ss = g_pFuncs->RevertSecurityContext (&pAS->_hctxt);
if (!SEC_SUCCESS(ss)) {
fprintf (stderr, "Revert failed: %u\n", ss);
return(FALSE);
}

return(TRUE);
}

BOOL InitSession (DWORD dwKey)
/*++

 Routine Description:

    Initializes the context associated with a key and adds it to the
collection.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
PAUTH_SEQ pAS;

pAS = (PAUTH_SEQ) malloc (sizeof (AUTH_SEQ));
if (NULL == pAS)
return(FALSE);

pAS->_fNewConversation = TRUE;
pAS->_fHaveCredHandle = FALSE;
pAS->_fHaveCtxtHandle = FALSE;

if (!AddEntry (dwKey, (PVOID)pAS))  {
free (pAS);
return(FALSE);
}

return(TRUE);
}

BOOL TermSession (DWORD dwKey)
/*++

 Routine Description:

    Releases the resources associated with a key and removes it from
the collection.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
PAUTH_SEQ pAS;

if (!DeleteEntry (dwKey, (LPVOID*)&pAS))
return(FALSE);

if (pAS->_fHaveCtxtHandle)
g_pFuncs->DeleteSecurityContext (&pAS->_hctxt);

if (pAS->_fHaveCredHandle)
g_pFuncs->FreeCredentialHandle (&pAS->_hcred);

free (pAS);

return(TRUE);
}


README.TXT

Portable Systems Group
MSV1_0 SubAuthentication DLL Design Note
Revision 1.3, March 7, 1996

1. INTRODUCTION
2. INTERFACE TO A SUBAUTHENTICATION DLL
3. REGISTERING A SUBAUTHENTICATION DLL
4. REQUESTING A SUBAUTHENTICATION DLL

1. Introduction

This document describes  the purpose of and the interface to a
SubAuthentication DLL for the MSV1_0 authentication package.

The MSV1_0 authentication package is the standard LSA authentication
package for Windows NT.  It provides or supports:

  Authentication of users in the SAM database.
  Pass-Thru authentication of users in trusted domains.

Windows NT allows SubAuthentication DLLs to be used in conjunction
with the MSV1_0 authentication package.  A SubAuthentication DLL
allows the authentication and validation criteria stored in SAM to be
replaced for particular subsystems that use the MSV1_0 authentication
package.  For instance, a particular server might supply a SubAuthentication
DLL that validates a user s password via a different algorithm, uses a
different granularity of logon hours, and/or specifies workstation restrictions
in a different format.

All of this can be accomplished using SubAuthentication DLLs without
sacrificing use of the SAM database (and losing its administration tools) or
losing pass-thru authentication.

2. Interface to a SubAuthentication DLL

There are two interfaces that may be supported by SubAuthentication DLLs.
The first is Msv1_0SubAuthenticationRoutine, which is called for
SubAuthentication packages other than package zero. These
SubAuthentication DLLs are called after the correct Domain Controller has
been located and the user to be authenticated has been looked up in the
SAM database.  No attributes of the user will be validated by the MSV1_0
authentication package.  That is the responsibility of the SubAuthentication
DLL.  The SubAuthentication DLL must contain a procedure named
Msv1_0SubAuthenticationRoutine with the following interface:

NTSTATUS
NTAPI
Msv1_0SubAuthenticationRoutine(
    IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
    IN PVOID LogonInformation,
    IN ULONG Flags,
    IN PUSER_ALL_INFORMATION UserAll,
    OUT PULONG WhichFields,
    OUT PULONG UserFlags,
    OUT PBOOLEAN Authoritative,
    OUT PLARGE_INTEGER LogoffTime,
    OUT PLARGE_INTEGER KickoffTime
);

The second SubAuthentication interface is Msv1_0SubAuthenticationFilter,
which is only called for SubAuthentication DLL zero.  In this case, after the
MSV1_0 authentication package has validated a logon (including network,
interactive, service, and batch logons) it will call the filter routine to do
additional validation. The filter routine may return success, indicating that
the logon should proceed, or failure, indicating that the the additional
validation failed.  In addition, the filter routine may modify the
UserParameters field in the USER_ALL_INFORMATION structure and set
the USER_ALL_PARAMETRS flag in the WhichFields parameter to
indicate that the change should be written to the user object.

NTSTATUS
NTAPI
Msv1_0SubAuthenticationFilter(
    IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
    IN PVOID LogonInformation,
    IN ULONG Flags,
    IN PUSER_ALL_INFORMATION UserAll,
    OUT PULONG WhichFields,
    OUT PULONG UserFlags,
    OUT PBOOLEAN Authoritative,
    OUT PLARGE_INTEGER LogoffTime,
    OUT PLARGE_INTEGER KickoffTime
);

3. Registering a SubAuthentication DLL
Each SubAuthentication DLL is assigned a DLL number in the range 0
through 255.  The DLL number is used to associate the subsystem calling
LsaLogonUser with the appropriate SubAuthentication DLL.

DLL number 0 is reserved to indicate that the
SubAuthentication Filter is to be used. It allows the package to
do additional password or logon validation on top of what
MSV1_0 normally provides. DLL numbers 1 through 127 are reserved for Microsoft.
DLL numbers 128 through 255 are available to ISVs.  ISVs can be assigned a DLL
number by Microsoft by sending email to subauth@microsoft.com.  Registering
your subauthentication pacakge with Microsoft prevents collision of package IDs
when multiple subauthentication packages are installed on a system.

Microsoft will not assign the value of 255 for any subauthentication DLL.
If you are developing a subauthentication DLL for use only within your company
or facility, you can use the subauthentication ID number 255.  In this case,
it is not necessary to register your subauthentication package with
Microsoft.

Once the ISV has picked a DLL number, the DLL can be registered
under the registry key
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0.
If the key doesn't exist, the ISV's installation procedure
should create it.  Under that key, the ISV should create a value named
AuthN where N is the DLL number (e.g., Auth128).

The value should be a REG_SZ and specify the name of the DLL
which must be in the default DLL load path.  For instance,

  Auth128=SubAuth

The MSV1_0 authentication package will load the named DLL
the first time the SubAuthentication DLL is requested.

4. Requesting a SubAuthentication DLL

A subsystem can request a particular SubAuthentication DLL when calling
LsaLogonUser.  The subsystem calls the MSV1_0 authentication package
(as described in the LSAAUTH.HLP file in the Windows NT DDK) passing
in the MSV1_0_LM20_LOGON structure.

typedef struct _MSV1_0_LM20_LOGON {
    MSV1_0_LOGON_SUBMIT_TYPE MessageType;
    UNICODE_STRING LogonDomainName;
    UNICODE_STRING UserName;
    UNICODE_STRING Workstation;
    UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH];
    STRING CaseSensitiveChallengeResponse;
    STRING CaseInsensitiveChallengeResponse;
    ULONG ParameterControl;
} MSV1_0_LM20_LOGON, * PMSV1_0_LM20_LOGON;

The MessageType field must be set to MsV1_0NetworkLogon (Interactive
logons may not be authenticated by a SubAuthentication DLL).

The LogonDomainName field should be set to the domain name of the
domain containing the SAM database to be used for authentication.  The
MSV1_0 authentication package and the Netlogon Service will pass thru the
authentication request to that domain.  The SubAuthentication DLL will be
called on a domain controller in the domain.

The UserName field must specify the name of a user in the SAM database
on that domain.

The Workstation, ChallengeToClient, CaseSensitiveChallengeResponse,
and CaseInsensitiveChallengeResponse fields may be set to any
SubAuthentication DLL specific values.  They will be ignored by the
MSV1_0 authentication package.

The ParameterControl field should be set as follows.  Set the various control
flags as appropriate.  Set the most significant byte of Parameter control to
the DLL number of the SubAuthentication DLL to use.

#define MSV1_0_CLEARTEXT_PASSWORD_ALLOWED    0x02
#define MSV1_0_UPDATE_LOGON_STATISTICS       0x04
#define MSV1_0_RETURN_USER_PARAMETERS        0x08
#define MSV1_0_DONT_TRY_GUEST_ACCOUNT        0x10

//
// The high order byte is a value indicating the SubAuthentication DLL.
//  Zero indicates no SubAuthentication DLL.
//
#define MSV1_0_SUBAUTHENTICATION_DLL             0xFF000000
#define MSV1_0_SUBAUTHENTICATION_DLL_SHIFT       24


SUBAUTH.C

/*++

Copyright (c) 1987-1996  Microsoft Corporation

Module Name:

    subauth.c

Abstract:

    Sample SubAuthentication Package.

Author:

    Cliff Van Dyke (cliffv) 23-May-1994

Revisions:

    Andy Herron (andyhe)    21-Jun-1994  Added code to read domain/user info

Environment:

    User mode only.
    Contains NT-specific code.
    Requires ANSI C extensions: slash-slash comments, long external names.

Revision History:

--*/

#if ( _MSC_VER >= 800 )
#pragma warning ( 3 : 4100 ) // enable "Unreferenced formal parameter"
#pragma warning ( 3 : 4219 ) // enable "trailing ',' used for variable argument list"
#endif

#define WIN32_NO_STATUS
#include <windef.h>
#undef WIN32_NO_STATUS
#include <windows.h>
#include <lmcons.h>
#include <lmaccess.h>
#include <lmapibuf.h>
#include "subauth.h"

BOOLEAN
EqualComputerName(
    IN PUNICODE_STRING String1,
    IN PUNICODE_STRING String2
    );

NTSTATUS
QuerySystemTime (
    OUT PLARGE_INTEGER SystemTime
    );

BOOL
GetPasswordExpired(
    IN LARGE_INTEGER PasswordLastSet,
    IN LARGE_INTEGER MaxPasswordAge
    );

NTSTATUS
AccountRestrictions(
    IN ULONG UserRid,
    IN PUNICODE_STRING LogonWorkStation,
    IN PUNICODE_STRING WorkStations,
    IN PLOGON_HOURS LogonHours,
    OUT PLARGE_INTEGER LogoffTime,
    OUT PLARGE_INTEGER KickoffTime
    );

LARGE_INTEGER
NetpSecondsToDeltaTime(
    IN ULONG Seconds
    );

VOID
InitUnicodeString(
    OUT PUNICODE_STRING DestinationString,
    IN PCWSTR SourceString OPTIONAL
    );

VOID
CopyUnicodeString(
    OUT PUNICODE_STRING DestinationString,
    IN PUNICODE_STRING SourceString OPTIONAL
    );

NTSTATUS
NTAPI
Msv1_0SubAuthenticationRoutine (
    IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
    IN PVOID LogonInformation,
    IN ULONG Flags,
    IN PUSER_ALL_INFORMATION UserAll,
    OUT PULONG WhichFields,
    OUT PULONG UserFlags,
    OUT PBOOLEAN Authoritative,
    OUT PLARGE_INTEGER LogoffTime,
    OUT PLARGE_INTEGER KickoffTime
)
/*++

Routine Description:

    The subauthentication routine does client/server specific authentication
    of a user. The credentials of the user are passed in addition to all the
    information from SAM defining the user. This routine decides whether to
    let the user log on.

Arguments:

    LogonLevel -- Specifies the level of information given in
        LogonInformation.

    LogonInformation -- Specifies the description for the user
        logging on.  The LogonDomainName field should be ignored.

    Flags - Flags describing the circumstances of the logon.

        MSV1_0_PASSTHRU -- This is a PassThru authenication.  (i.e., the
            user isn't connecting to this machine.)
        MSV1_0_GUEST_LOGON -- This is a retry of the logon using the GUEST
            user account.

    UserAll -- The description of the user as returned from SAM.

    WhichFields -- Returns which fields from UserAllInfo are to be written
        back to SAM.  The fields will only be written if MSV returns success
        to it's caller.  Only the following bits are valid.

        USER_ALL_PARAMETERS - Write UserAllInfo->Parameters back to SAM.  If
            the size of the buffer is changed, Msv1_0SubAuthenticationRoutine
            must delete the old buffer using MIDL_user_free() and reallocate the
            buffer using MIDL_user_allocate().

    UserFlags -- Returns UserFlags to be returned from LsaLogonUser in the
        LogonProfile.  The following bits are currently defined:

            LOGON_GUEST -- This was a guest logon
            LOGON_NOENCRYPTION -- The caller didn't specify encrypted credentials

        SubAuthentication packages should restrict themselves to returning
        bits in the high order byte of UserFlags.  However, this convention
        isn't enforced giving the SubAuthentication package more flexibility.

    Authoritative -- Returns whether the status returned is an
        authoritative status which should be returned to the original
        caller.  If not, this logon request may be tried again on another
        domain controller.  This parameter is returned regardless of the
        status code.

    LogoffTime - Receives the time at which the user should log off the
        system.  This time is specified as a GMT relative NT system time.

    KickoffTime - Receives the time at which the user should be kicked
        off the system. This time is specified as a GMT relative system
        time.  Specify, a full scale positive number if the user isn't to
        be kicked off.

Return Value:

    STATUS_SUCCESS: if there was no error.

    STATUS_NO_SUCH_USER: The specified user has no account.
    STATUS_WRONG_PASSWORD: The password was invalid.

    STATUS_INVALID_INFO_CLASS: LogonLevel is invalid.
    STATUS_ACCOUNT_LOCKED_OUT: The account is locked out
    STATUS_ACCOUNT_DISABLED: The account is disabled
    STATUS_ACCOUNT_EXPIRED: The account has expired.
    STATUS_PASSWORD_MUST_CHANGE: Account is marked as Password must change
        on next logon.
    STATUS_PASSWORD_EXPIRED: The Password is expired.
    STATUS_INVALID_LOGON_HOURS - The user is not authorized to log on at
        this time.
    STATUS_INVALID_WORKSTATION - The user is not authorized to log on to
        the specified workstation.

--*/
{
    NTSTATUS Status;
    ULONG UserAccountControl;
    LARGE_INTEGER LogonTime;
    UNICODE_STRING LocalWorkstation;

    PNETLOGON_NETWORK_INFO LogonNetworkInfo;

    //
    // Check whether the SubAuthentication package supports this type
    //  of logon.
    //

    *Authoritative = TRUE;
    *UserFlags = 0;
    *WhichFields = 0;

    (VOID) QuerySystemTime( &LogonTime );

    switch ( LogonLevel ) {
    case NetlogonInteractiveInformation:
    case NetlogonServiceInformation:

        //
        // This SubAuthentication package only supports network logons.
        //

        return STATUS_INVALID_INFO_CLASS;

    case NetlogonNetworkInformation:

        //
        // This SubAuthentication package doesn't support access via machine
        // accounts.
        //

        UserAccountControl = USER_NORMAL_ACCOUNT;

        //
        // Local user (Temp Duplicate) accounts are only used on the machine
        // being directly logged onto.
        // (Nor are interactive or service logons allowed to them.)
        //

        if ( (Flags & MSV1_0_PASSTHRU) == 0 ) {
            UserAccountControl |= USER_TEMP_DUPLICATE_ACCOUNT;
        }

        LogonNetworkInfo = (PNETLOGON_NETWORK_INFO) LogonInformation;

        break;

    default:
        *Authoritative = TRUE;
        return STATUS_INVALID_INFO_CLASS;
    }

    //
    // If the account type isn't allowed,
    //  Treat this as though the User Account doesn't exist.
    //

    if ( (UserAccountControl & UserAll->UserAccountControl) == 0 ) {
        *Authoritative = FALSE;
        Status = STATUS_NO_SUCH_USER;
        goto Cleanup;
    }

    //
    // This SubAuthentication package doesn't allow guest logons.
    //
    if ( Flags & MSV1_0_GUEST_LOGON ) {
        *Authoritative = FALSE;
        Status = STATUS_NO_SUCH_USER;
        goto Cleanup;
    }

    //
    // Ensure the account isn't locked out.
    //

    if ( UserAll->UserId != DOMAIN_USER_RID_ADMIN &&
         (UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) ) {

        //
        // Since the UI strongly encourages admins to disable user
        // accounts rather than delete them.  Treat disabled acccount as
        // non-authoritative allowing the search to continue for other
        // accounts by the same name.
        //
        if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
            *Authoritative = FALSE;
        } else {
            *Authoritative = TRUE;
        }
        Status = STATUS_ACCOUNT_LOCKED_OUT;
        goto Cleanup;
    }

    //
    // Check the password.
    //

    if ( FALSE /* VALIDATE THE USER'S PASSWORD HERE */ ) {

        Status = STATUS_WRONG_PASSWORD;

        //
        // Since the UI strongly encourages admins to disable user
        // accounts rather than delete them.  Treat disabled acccount as
        // non-authoritative allowing the search to continue for other
        // accounts by the same name.
        //
        if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
            *Authoritative = FALSE;
        } else {
            *Authoritative = TRUE;
        }

        goto Cleanup;
    }

    //
    // Prevent some things from effecting the Administrator user
    //

    if (UserAll->UserId == DOMAIN_USER_RID_ADMIN) {

        //
        //  The administrator account doesn't have a forced logoff time.
        //

        LogoffTime->HighPart = 0x7FFFFFFF;
        LogoffTime->LowPart = 0xFFFFFFFF;

        KickoffTime->HighPart = 0x7FFFFFFF;
        KickoffTime->LowPart = 0xFFFFFFFF;

    } else {

        //
        // Check if the account is disabled.
        //

        if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED ) {
            //
            // Since the UI strongly encourages admins to disable user
            // accounts rather than delete them.  Treat disabled acccount as
            // non-authoritative allowing the search to continue for other
            // accounts by the same name.
            //
            *Authoritative = FALSE;
            Status = STATUS_ACCOUNT_DISABLED;
            goto Cleanup;
        }

        //
        // Check if the account has expired.
        //

        if ( UserAll->AccountExpires.QuadPart != 0 &&
             LogonTime.QuadPart >= UserAll->AccountExpires.QuadPart ) {
            *Authoritative = TRUE;
            Status = STATUS_ACCOUNT_EXPIRED;
            goto Cleanup;
        }

#if 1

    //
    //  If your using SAM's password expiration date, use this code, otherwise
    //  use the code below and supply your own password set date...
    //

        //
        // The password is valid, check to see if the password is expired.
        //  (SAM will have appropriately set PasswordMustChange to reflect
        //  USER_DONT_EXPIRE_PASSWORD)
        //
        // If the password checked above is not the SAM password, you may
        // want to consider not checking the SAM password expiration times here.
        //

        if ( LogonTime.QuadPart >= UserAll->PasswordMustChange.QuadPart ) {

            if ( UserAll->PasswordLastSet.QuadPart == 0 ) {
                Status = STATUS_PASSWORD_MUST_CHANGE;
            } else {
                Status = STATUS_PASSWORD_EXPIRED;
            }
            *Authoritative = TRUE;
            goto Cleanup;
        }

#else

        //
        // Response is correct. So, check if the password has expired or not
        //

        if (! (UserAll->UserAccountControl & USER_DONT_EXPIRE_PASSWORD)) {
            LARGE_INTEGER MaxPasswordAge;
            MaxPasswordAge.HighPart = 0x7FFFFFFF;
            MaxPasswordAge.LowPart = 0xFFFFFFFF;

            //
            // PasswordDateSet should be modified to hold the last date the
            // user's password was set.
            //

            PasswordDateSet.LowPart = 0;
            PasswordDateSet.HighPart = 0;

            if ( GetPasswordExpired( PasswordDateSet,
                        MaxPasswordAge )) {

                Status = STATUS_PASSWORD_EXPIRED;
                goto Cleanup;
            }
        }

#endif

#if 1

    //
    // Validate the workstation the user logged on from.
    //
    // Ditch leading \\ on workstation name before passing it to SAM.
    //

    LocalWorkstation = LogonNetworkInfo->Identity.Workstation;
    if ( LocalWorkstation.Length > 0 &&
         LocalWorkstation.Buffer[0] == L'\\' &&
         LocalWorkstation.Buffer[1] == L'\\' ) {
        LocalWorkstation.Buffer += 2;
        LocalWorkstation.Length -= 2*sizeof(WCHAR);
        LocalWorkstation.MaximumLength -= 2*sizeof(WCHAR);
    }

    //
    //  To validate the user's logon hours as SAM does it, use this code,
    //  otherwise, supply your own checks below this code.
    //

    Status = AccountRestrictions( UserAll->UserId,
                                  &LocalWorkstation,
                                  (PUNICODE_STRING) &UserAll->WorkStations,
                                  &UserAll->LogonHours,
                                  LogoffTime,
                                  KickoffTime );

    if ( !NT_SUCCESS( Status )) {
        goto Cleanup;
    }

#else

        //
        // Validate the user's logon hours.
        //

        if ( TRUE /* VALIDATE THE LOGON HOURS */ ) {

            //
            // All times are allowed, so there's no logoff
            // time.  Return forever for both logofftime and
            // kickofftime.
            //

            LogoffTime->HighPart = 0x7FFFFFFF;
            LogoffTime->LowPart = 0xFFFFFFFF;

            KickoffTime->HighPart = 0x7FFFFFFF;
            KickoffTime->LowPart = 0xFFFFFFFF;
        } else {
            Status = STATUS_INVALID_LOGON_HOURS;
            *Authoritative = TRUE;
            goto Cleanup;
        }
#endif

        //
        // Validate if the user can log on from this workstation.
        //  (Supply subauthentication package specific code here.)

        if ( LogonNetworkInfo->Identity.Workstation.Buffer == NULL ) {
            Status = STATUS_INVALID_WORKSTATION;
            *Authoritative = TRUE;
            goto Cleanup;
        }
    }

    //
    // The user is valid.
    //

    *Authoritative = TRUE;
    Status = STATUS_SUCCESS;

    //
    // Cleanup up before returning.
    //

Cleanup:

    return Status;

}  // Msv1_0SubAuthenticationRoutine

BOOL
GetPasswordExpired (
    IN LARGE_INTEGER PasswordLastSet,
    IN LARGE_INTEGER MaxPasswordAge
    )

/*++

Routine Description:

    This routine returns true if the password is expired, false otherwise.

Arguments:

    PasswordLastSet - Time when the password was last set for this user.

    MaxPasswordAge - Maximum password age for any password in the domain.

Return Value:

    Returns true if password is expired.  False if not expired.

--*/
{
    LARGE_INTEGER PasswordMustChange;
    NTSTATUS Status;
    BOOLEAN rc;
    LARGE_INTEGER TimeNow;

    //
    // Compute the expiration time as the time the password was
    // last set plus the maximum age.
    //

    if ( PasswordLastSet.QuadPart < 0 || MaxPasswordAge.QuadPart > 0 ) {

        rc = TRUE;      // default for invalid times is that it is expired.

    } else {

        __try {

            PasswordMustChange.QuadPart =
                PasswordLastSet.QuadPart - MaxPasswordAge.QuadPart;
            //
            // Limit the resultant time to the maximum valid absolute time
            //

            if ( PasswordMustChange.QuadPart < 0 ) {

                rc = FALSE;

            } else {

                Status = QuerySystemTime( &TimeNow );
                if (NT_SUCCESS(Status)) {

                    if ( TimeNow.QuadPart >= PasswordMustChange.QuadPart ) {
                        rc = TRUE;

                    } else {

                        rc = FALSE;
                    }
                } else {
                    rc = FALSE;     // won't fail if QuerySystemTime failed.
                }
            }

        } __except(EXCEPTION_EXECUTE_HANDLER) {

            rc = TRUE;
        }
    }

    return rc;

}  // GetPasswordExpired

NTSTATUS
QuerySystemTime (
    OUT PLARGE_INTEGER SystemTime
    )

/*++

Routine Description:

    This function returns the absolute system time. The time is in units of
    100nsec ticks since the base time which is midnight January 1, 1601.

Arguments:

    SystemTime - Supplies the address of a variable that will receive the
        current system time.

Return Value:

    STATUS_SUCCESS is returned if the service is successfully executed.

    STATUS_ACCESS_VIOLATION is returned if the output parameter for the
        system time cannot be written.

--*/

{
    SYSTEMTIME CurrentTime;

    GetSystemTime( &CurrentTime );

    if ( !SystemTimeToFileTime( &CurrentTime, (LPFILETIME) SystemTime ) ) {
        return STATUS_ACCESS_VIOLATION;
    }

    return STATUS_SUCCESS;
}

NTSTATUS
SampMatchworkstation(
    IN PUNICODE_STRING LogonWorkStation,
    IN PUNICODE_STRING WorkStations
    )

/*++

Routine Description:

    Check if the given workstation is a member of the list of workstations
    given.

Arguments:

    LogonWorkStations - UNICODE name of the workstation that the user is
        trying to log into.

    WorkStations - API list of workstations that the user is allowed to
        log into.

Return Value:

    STATUS_SUCCESS - The user is allowed to log into the workstation.

--*/
{
    PWCHAR          WorkStationName;
    UNICODE_STRING  Unicode;
    NTSTATUS        NtStatus;
    WCHAR           Buffer[256];
    USHORT          LocalBufferLength = 256;
    UNICODE_STRING  WorkStationsListCopy;
    BOOLEAN         BufferAllocated = FALSE;
    PWCHAR          TmpBuffer;

    //
    // Local workstation is always allowed
    // If WorkStations field is 0 everybody is allowed
    //

    if ( ( LogonWorkStation == NULL ) ||
        ( LogonWorkStation->Length == 0 ) ||
        ( WorkStations->Length == 0 ) ) {

        return( STATUS_SUCCESS );
    }

    //
    // Assume failure; change status only if we find the string.
    //

    NtStatus = STATUS_INVALID_WORKSTATION;

    //
    // WorkStationApiList points to our current location in the list of
    // WorkStations.
    //

    if ( WorkStations->Length > LocalBufferLength ) {

        WorkStationsListCopy.Buffer = LocalAlloc( 0, WorkStations->Length );
        BufferAllocated = TRUE;

        if ( WorkStationsListCopy.Buffer == NULL ) {
            NtStatus = STATUS_INSUFFICIENT_RESOURCES;
            return( NtStatus );
        }

        WorkStationsListCopy.MaximumLength = WorkStations->Length;

    } else {

        WorkStationsListCopy.Buffer = Buffer;
        WorkStationsListCopy.MaximumLength = LocalBufferLength;
    }

    CopyUnicodeString( &WorkStationsListCopy, WorkStations );

    //
    // wcstok requires a string the first time it's called, and NULL
    // for all subsequent calls.  Use a temporary variable so we
    // can do this.
    //

    TmpBuffer = WorkStationsListCopy.Buffer;

    while( WorkStationName = wcstok(TmpBuffer, L",") ) {

        TmpBuffer = NULL;
        InitUnicodeString( &Unicode, WorkStationName );
        if (EqualComputerName( &Unicode, LogonWorkStation )) {
            NtStatus = STATUS_SUCCESS;
            break;
        }
    }

    if ( BufferAllocated ) {
        LocalFree( WorkStationsListCopy.Buffer );
    }

    return( NtStatus );
}

NTSTATUS
AccountRestrictions(
    IN ULONG UserRid,
    IN PUNICODE_STRING LogonWorkStation,
    IN PUNICODE_STRING WorkStations,
    IN PLOGON_HOURS LogonHours,
    OUT PLARGE_INTEGER LogoffTime,
    OUT PLARGE_INTEGER KickoffTime
    )

/*++

Routine Description:

    Validate a user's ability to log on at this time and at the workstation
    being logged onto.

Arguments:

    UserRid - The user id of the user to operate on.

    LogonWorkStation - The name of the workstation the logon is being
        attempted at.

    WorkStations - The list of workstations the user may log on to.  This
        information comes from the user's account information.  It must
        be in API list format.

    LogonHours - The times the user may logon.  This information comes
        from the user's account information.

    LogoffTime - Receives the time at which the user should log off the
        system.

    KickoffTime - Receives the time at which the user should be kicked
        off the system.

Return Value:

    STATUS_SUCCESS - Logon is permitted.

    STATUS_INVALID_LOGON_HOURS - The user is not authorized to log on at
        this time.

    STATUS_INVALID_WORKSTATION - The user is not authorized to log on to
        the specified workstation.

--*/
{

    static BOOLEAN GetForceLogoff = TRUE;
    static LARGE_INTEGER ForceLogoff = { 0x7fffffff, 0xFFFFFFF};

#define MILLISECONDS_PER_WEEK 7 * 24 * 60 * 60 * 1000

    SYSTEMTIME              CurrentTimeFields;
    LARGE_INTEGER           CurrentTime, CurrentUTCTime;
    LARGE_INTEGER           MillisecondsIntoWeekXUnitsPerWeek;
    LARGE_INTEGER           LargeUnitsIntoWeek;
    LARGE_INTEGER           Delta100Ns;
    NTSTATUS                NtStatus = STATUS_SUCCESS;
    ULONG                   CurrentMsIntoWeek;
    ULONG                   LogoffMsIntoWeek;
    ULONG                   DeltaMs;
    ULONG                   MillisecondsPerUnit;
    ULONG                   CurrentUnitsIntoWeek;
    ULONG                   LogoffUnitsIntoWeek;
    USHORT                  i;
    TIME_ZONE_INFORMATION   TimeZoneInformation;
    DWORD TimeZoneId;
    LARGE_INTEGER           BiasIn100NsUnits;
    LONG                    BiasInMinutes;

    //
    // Only check for users other than the builtin ADMIN
    //

    if ( UserRid != DOMAIN_USER_RID_ADMIN) {

        //
        // Scan to make sure the workstation being logged into is in the
        // list of valid workstations - or if the list of valid workstations
        // is null, which means that all are valid.
        //

        NtStatus = SampMatchworkstation( LogonWorkStation, WorkStations );

        if ( NT_SUCCESS( NtStatus ) ) {

            //
            // Check to make sure that the current time is a valid time to log
            // on in the LogonHours.
            //
            // We need to validate the time taking into account whether we are
            // in daylight savings time or standard time.  Thus, if the logon
            // hours specify that we are able to log on between 9am and 5pm,
            // this means 9am to 5pm standard time during the standard time
            // period, and 9am to 5pm daylight savings time when in the
            // daylight savings time.  Since the logon hours stored by SAM are
            // independent of daylight savings time, we need to add in the
            // difference between standard time and daylight savings time to
            // the current time before checking whether this time is a valid
            // time to log on.  Since this difference (or bias as it is called)
            // is actually held in the form
            //
            // Standard time = Daylight savings time + Bias
            //
            // the Bias is a negative number.  Thus we actually subtract the
            // signed Bias from the Current Time.

            //
            // First, get the Time Zone Information.
            //

            TimeZoneId = GetTimeZoneInformation(
                             (LPTIME_ZONE_INFORMATION) &TimeZoneInformation
                             );

            //
            // Next, get the appropriate bias (signed integer in minutes) to subtract from
            // the Universal Time Convention (UTC) time returned by NtQuerySystemTime
            // to get the local time.  The bias to be used depends whether we're
            // in Daylight Savings time or Standard Time as indicated by the
            // TimeZoneId parameter.
            //
            // local time  = UTC time - bias in 100Ns units
            //

            switch (TimeZoneId) {

            case TIME_ZONE_ID_UNKNOWN:

                //
                // There is no differentiation between standard and
                // daylight savings time.  Proceed as for Standard Time
                //

                BiasInMinutes = TimeZoneInformation.StandardBias;
                break;

            case TIME_ZONE_ID_STANDARD:

                BiasInMinutes = TimeZoneInformation.StandardBias;
                break;

            case TIME_ZONE_ID_DAYLIGHT:

                BiasInMinutes = TimeZoneInformation.DaylightBias;
                break;

            default:

                //
                // Something is wrong with the time zone information.  Fail
                // the logon request.
                //

                NtStatus = STATUS_INVALID_LOGON_HOURS;
                break;
            }

            if (NT_SUCCESS(NtStatus)) {

                //
                // Convert the Bias from minutes to 100ns units
                //

                BiasIn100NsUnits.QuadPart = ((LONGLONG)BiasInMinutes)
                                            * 60 * 10000000;

                //
                // Get the UTC time in 100Ns units used by Windows Nt.  This
                // time is GMT.
                //

                NtStatus = QuerySystemTime( &CurrentUTCTime );
            }

            if ( NT_SUCCESS( NtStatus ) ) {

                CurrentTime.QuadPart = CurrentUTCTime.QuadPart -
                              BiasIn100NsUnits.QuadPart;

                FileTimeToSystemTime( (PFILETIME)&CurrentTime, &CurrentTimeFields );

                CurrentMsIntoWeek = (((( CurrentTimeFields.wDayOfWeek * 24 ) +
                                       CurrentTimeFields.wHour ) * 60 +
                                       CurrentTimeFields.wMinute ) * 60 +
                                       CurrentTimeFields.wSecond ) * 1000 +
                                       CurrentTimeFields.wMilliseconds;

                MillisecondsIntoWeekXUnitsPerWeek.QuadPart =
                    ((LONGLONG)CurrentMsIntoWeek) *
                    ((LONGLONG)LogonHours->UnitsPerWeek);

                LargeUnitsIntoWeek.QuadPart =
                    MillisecondsIntoWeekXUnitsPerWeek.QuadPart / ((ULONG) MILLISECONDS_PER_WEEK);

                CurrentUnitsIntoWeek = LargeUnitsIntoWeek.LowPart;

                if ( !( LogonHours->LogonHours[ CurrentUnitsIntoWeek / 8] &
                    ( 0x01 << ( CurrentUnitsIntoWeek % 8 ) ) ) ) {

                    NtStatus = STATUS_INVALID_LOGON_HOURS;

                } else {

                    //
                    // Determine the next time that the user is NOT supposed to be logged
                    // in, and return that as LogoffTime.
                    //

                    i = 0;
                    LogoffUnitsIntoWeek = CurrentUnitsIntoWeek;

                    do {

                        i++;

                        LogoffUnitsIntoWeek = ( LogoffUnitsIntoWeek + 1 ) % LogonHours->UnitsPerWeek;

                    } while ( ( i <= LogonHours->UnitsPerWeek ) &&
                        ( LogonHours->LogonHours[ LogoffUnitsIntoWeek / 8 ] &
                        ( 0x01 << ( LogoffUnitsIntoWeek % 8 ) ) ) );

                    if ( i > LogonHours->UnitsPerWeek ) {

                        //
                        // All times are allowed, so there's no logoff
                        // time.  Return forever for both LogoffTime and
                        // KickoffTime.
                        //

                        LogoffTime->HighPart = 0x7FFFFFFF;
                        LogoffTime->LowPart = 0xFFFFFFFF;

                        KickoffTime->HighPart = 0x7FFFFFFF;
                        KickoffTime->LowPart = 0xFFFFFFFF;

                    } else {

                        //
                        // LogoffUnitsIntoWeek points at which time unit the
                        // user is to log off.  Calculate actual time from
                        // the unit, and return it.
                        //
                        // CurrentTimeFields already holds the current
                        // time for some time during this week; just adjust
                        // to the logoff time during this week and convert
                        // to time format.
                        //

                        MillisecondsPerUnit = MILLISECONDS_PER_WEEK / LogonHours->UnitsPerWeek;

                        LogoffMsIntoWeek = MillisecondsPerUnit * LogoffUnitsIntoWeek;

                        if ( LogoffMsIntoWeek < CurrentMsIntoWeek ) {

                            DeltaMs = MILLISECONDS_PER_WEEK - ( CurrentMsIntoWeek - LogoffMsIntoWeek );

                        } else {

                            DeltaMs = LogoffMsIntoWeek - CurrentMsIntoWeek;
                        }

                        Delta100Ns.QuadPart = (LONGLONG) DeltaMs * 10000;

                        LogoffTime->QuadPart = CurrentUTCTime.QuadPart +
                                      Delta100Ns.QuadPart;

                        //
                        // Grab the domain's ForceLogoff time.
                        //

                        if ( GetForceLogoff ) {
                            NET_API_STATUS NetStatus;
                            LPUSER_MODALS_INFO_0 UserModals0;

                            NetStatus = NetUserModalsGet( NULL,
                                                          0,
                                                          (LPBYTE *)&UserModals0 );

                            if ( NetStatus == 0 ) {
                                GetForceLogoff = FALSE;

                                ForceLogoff = NetpSecondsToDeltaTime( UserModals0->usrmod0_force_logoff );

                                NetApiBufferFree( UserModals0 );
                            }
                        }
                        //
                        // Subtract Domain->ForceLogoff from LogoffTime, and return
                        // that as KickoffTime.  Note that Domain->ForceLogoff is a
                        // negative delta.  If its magnitude is sufficiently large
                        // (in fact, larger than the difference between LogoffTime
                        // and the largest positive large integer), we'll get overflow
                        // resulting in a KickOffTime that is negative.  In this
                        // case, reset the KickOffTime to this largest positive
                        // large integer (i.e. "never") value.
                        //

                        KickoffTime->QuadPart = LogoffTime->QuadPart - ForceLogoff.QuadPart;

                        if (KickoffTime->QuadPart < 0) {

                            KickoffTime->HighPart = 0x7FFFFFFF;
                            KickoffTime->LowPart = 0xFFFFFFFF;
                        }
                    }
                }
            }
        }

    } else {

        //
        // Never kick administrators off
        //

        LogoffTime->HighPart  = 0x7FFFFFFF;
        LogoffTime->LowPart   = 0xFFFFFFFF;
        KickoffTime->HighPart = 0x7FFFFFFF;
        KickoffTime->LowPart  = 0xFFFFFFFF;
    }

    return( NtStatus );
}

LARGE_INTEGER
NetpSecondsToDeltaTime(
    IN ULONG Seconds
    )

/*++

Routine Description:

    Convert a number of seconds to an NT delta time specification

Arguments:

    Seconds - a positive number of seconds

Return Value:

    Returns the NT Delta time.  NT delta time is a negative number
        of 100ns units.

--*/

{
    LARGE_INTEGER DeltaTime;
    LARGE_INTEGER LargeSeconds;
    LARGE_INTEGER Answer;

    //
    // Special case TIMEQ_FOREVER (return a full scale negative)
    //

    if ( Seconds == TIMEQ_FOREVER ) {
        DeltaTime.LowPart = 0;
        DeltaTime.HighPart = (LONG) 0x80000000;

    //
    // Convert seconds to 100ns units simply by multiplying by 10000000.
    //
    // Convert to delta time by negating.
    //

    } else {

        LargeSeconds.LowPart = Seconds;
        LargeSeconds.HighPart = 0;

        Answer.QuadPart = LargeSeconds.QuadPart * 10000000;

          if ( Answer.QuadPart < 0 ) {
            DeltaTime.LowPart = 0;
            DeltaTime.HighPart = (LONG) 0x80000000;
        } else {
            DeltaTime.QuadPart = -Answer.QuadPart;
        }

    }

    return DeltaTime;

} // NetpSecondsToDeltaTime

BOOLEAN
EqualComputerName(
    IN PUNICODE_STRING String1,
    IN PUNICODE_STRING String2
    )
/*++

Routine Description:

    Compare two computer names for equality.

Arguments:

    String1 - Name of first computer.
    String2 - Name of second computer.

Return Value:

    TRUE if the names, converted to OEM, compare case-insensitively,
    FALSE if they don't compare or can't be converted to OEM.

--*/
{
    WCHAR Computer1[CNLEN+1];
    WCHAR Computer2[CNLEN+1];
    CHAR OemComputer1[CNLEN+1];
    CHAR OemComputer2[CNLEN+1];

    //
    // Make sure the names are not too long
    //

    if ((String1->Length > CNLEN*sizeof(WCHAR)) ||
        (String2->Length > CNLEN*sizeof(WCHAR))) {
        return(FALSE);

    }

    //
    // Copy them to null terminated strings
    //

    CopyMemory(
        Computer1,
        String1->Buffer,
        String1->Length
        );
    Computer1[String1->Length/sizeof(WCHAR)] = L'\0';

    CopyMemory(
        Computer2,
        String2->Buffer,
        String2->Length
        );
    Computer2[String2->Length/sizeof(WCHAR)] = L'\0';

    //
    // Convert the computer names to OEM
    //

    if (!CharToOemW(
            Computer1,
            OemComputer1
            )) {
        return(FALSE);
    }

    if (!CharToOemW(
            Computer2,
            OemComputer2
            )) {
        return(FALSE);
    }

    //
    // Do a case insensitive comparison of the oem computer names.
    //

    if (lstrcmpiA(OemComputer1, OemComputer2) == 0)
    {
        return(TRUE);
    }
    else
    {
        return(FALSE);
    }
}

VOID
InitUnicodeString(
    OUT PUNICODE_STRING DestinationString,
    IN PCWSTR SourceString OPTIONAL
    )

/*++

Routine Description:

    The InitUnicodeString function initializes an NT counted
    unicode string.  The DestinationString is initialized to point to
    the SourceString and the Length and MaximumLength fields of
    DestinationString are initialized to the length of the SourceString,
    which is zero if SourceString is not specified.

Arguments:

    DestinationString - Pointer to the counted string to initialize

    SourceString - Optional pointer to a null terminated unicode string that
        the counted string is to point to.

Return Value:

    None.

--*/

{
    ULONG Length;

    DestinationString->Buffer = (PWSTR)SourceString;
    if (SourceString != NULL) {
        Length = wcslen( SourceString ) * sizeof( WCHAR );
        DestinationString->Length = (USHORT)Length;
        DestinationString->MaximumLength = (USHORT)(Length + sizeof(UNICODE_NULL));
        }
    else {
        DestinationString->MaximumLength = 0;
        DestinationString->Length = 0;
        }
}

VOID
CopyUnicodeString(
    OUT PUNICODE_STRING DestinationString,
    IN PUNICODE_STRING SourceString OPTIONAL
    )

/*++

Routine Description:

    The CopyString function copies the SourceString to the
    DestinationString.  If SourceString is not specified, then
    the Length field of DestinationString is set to zero.  The
    MaximumLength and Buffer fields of DestinationString are not
    modified by this function.

    The number of bytes copied from the SourceString is either the
    Length of SourceString or the MaximumLength of DestinationString,
    whichever is smaller.

Arguments:

    DestinationString - Pointer to the destination string.

    SourceString - Optional pointer to the source string.

Return Value:

    None.

--*/

{
    UNALIGNED WCHAR *src, *dst;
    ULONG n;

    if (SourceString != NULL) {
        dst = DestinationString->Buffer;
        src = SourceString->Buffer;
        n = SourceString->Length;
        if ((USHORT)n > DestinationString->MaximumLength) {
            n = DestinationString->MaximumLength;
        }

        DestinationString->Length = (USHORT)n;
        CopyMemory(dst, src, n);
        if (DestinationString->Length < DestinationString->MaximumLength) {
            dst[n / sizeof(WCHAR)] = UNICODE_NULL;
        }

    } else {
        DestinationString->Length = 0;
    }

    return;
}

NTSTATUS
NTAPI
Msv1_0SubAuthenticationFilter (
    IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
    IN PVOID LogonInformation,
    IN ULONG Flags,
    IN PUSER_ALL_INFORMATION UserAll,
    OUT PULONG WhichFields,
    OUT PULONG UserFlags,
    OUT PBOOLEAN Authoritative,
    OUT PLARGE_INTEGER LogoffTime,
    OUT PLARGE_INTEGER KickoffTime
)
{
    return( Msv1_0SubAuthenticationRoutine(
                LogonLevel,
                LogonInformation,
                Flags,
                UserAll,
                WhichFields,
                UserFlags,
                Authoritative,
                LogoffTime,
                KickoffTime
                ) );
}
// subauth.c eof


README.TXT

Granting Administrator Access

The TAKEOWN sample implements a recovery scheme to give an Administrator
access to a file that has had its access denied to all.

This sample must be run from an Administrator account in order to perform
reliably.


TAKEOWN.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1993 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

/*++

Module Name:

    Takeown.c

Abstract:

    Implements a recovery scheme to give an Administrator access to a
    file that has been denied to all.

Environment:

    Must be run from an Administrator account in order to perform
    reliably.

--*/
#include <windows.h>
#include <stdio.h>
#include <malloc.h>

BOOL
AssertTakeOwnership(
    HANDLE TokenHandle
    );

BOOL
GetTokenHandle(
    PHANDLE TokenHandle
    );

BOOL
VariableInitialization();

PSID AliasAdminsSid = NULL;
PSID SeWorldSid;

static SID_IDENTIFIER_AUTHORITY    SepNtAuthority = SECURITY_NT_AUTHORITY;
static SID_IDENTIFIER_AUTHORITY    SepWorldSidAuthority   = SECURITY_WORLD_SID_AUTHORITY;

void main (int argc, char *argv[])
{

    BOOL Result;
    LPSTR lpFileName;
    SECURITY_DESCRIPTOR SecurityDescriptor;
    HANDLE TokenHandle;

    //
    // We expect a file...
    //
    if (argc <= 1) {

        printf("Must specify a file name");
        return;
    }

    lpFileName = argv[1];

    Result = VariableInitialization();

    if ( !Result ) {
        printf("Out of memory\n");
        return;
    }

    Result = GetTokenHandle( &TokenHandle );

    if ( !Result ) {

        //
        // This should not happen
        //

        printf("Unable to obtain the handle to our token, exiting\n");
        return;
    }

    //
    // Attempt to put a NULL Dacl on the object
    //

    InitializeSecurityDescriptor( &SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION );

    Result = SetSecurityDescriptorDacl (
                 &SecurityDescriptor,
                 TRUE,
                 NULL,
                 FALSE
                 );

    if ( !Result ) {
        printf("SetSecurityDescriptorDacl failed, error code = %d\n", GetLastError());
        printf("Exiting\n");
        return;
    }

    Result = SetFileSecurity(
                 lpFileName,
                 DACL_SECURITY_INFORMATION,
                 &SecurityDescriptor
                 );

    if ( Result ) {

        printf("Successful, protection removed\n");
        return;
    }

    //
    // That didn't work.
    //

    //
    // Attempt to make Administrator the owner of the file.
    //

    Result = SetSecurityDescriptorOwner (
                 &SecurityDescriptor,
                 AliasAdminsSid,
                 FALSE
                 );

    if ( !Result ) {
        printf("SetSecurityDescriptorOwner failed, lasterror = %d\n", GetLastError());
        return;
    }

    Result = SetFileSecurity(
                 lpFileName,
                 OWNER_SECURITY_INFORMATION,
                 &SecurityDescriptor
                 );

    if ( !Result ) {

        //
        // That didn't work either.
        //

        //
        // Assert TakeOwnership privilege, then try again.  Note that
        // since the privilege is only enabled for the duration of
        // this process, we don't have to worry about turning it off
        // again.
        //

        Result = AssertTakeOwnership( TokenHandle );

        if ( !Result ) {
            printf("Could not enable SeTakeOwnership privilege\n");
            printf("Log on as Administrator and try again\n");
            return;
        }

        Result = SetFileSecurity(
                     lpFileName,
                     OWNER_SECURITY_INFORMATION,
                     &SecurityDescriptor
                     );

        if ( !Result ) {

            printf("Unable to assign Administrator as owner\n");
            printf("Log on as Administrator and try again\n");
            return;

        }
    }

    //
    // Try to put a benign DACL onto the file again
    //

    Result = SetFileSecurity(
                 lpFileName,
                 DACL_SECURITY_INFORMATION,
                 &SecurityDescriptor
                 );

    if ( !Result ) {

        //
        // This should not happen.
        //

        printf("SetFileSecurity unexpectedly failed, error code = %d\n", GetLastError());

    } else {

        printf("Successful, protection removed\n");
        return;
    }
}

BOOL
GetTokenHandle(
    PHANDLE TokenHandle
    )
//
// This routine will open the current process and return
// a handle to its token.
//
// These handles will be closed for us when the process
// exits.
//
{

    HANDLE ProcessHandle;
    BOOL Result;

    ProcessHandle = OpenProcess(
                        PROCESS_QUERY_INFORMATION,
                        FALSE,
                        GetCurrentProcessId()
                        );

    if ( ProcessHandle == NULL ) {

        //
        // This should not happen
        //

        return( FALSE );
    }

    Result = OpenProcessToken (
                 ProcessHandle,
                 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                 TokenHandle
                 );

    if ( !Result ) {

        //
        // This should not happen
        //

        return( FALSE );

    }

    return( TRUE );
}

BOOL
AssertTakeOwnership(
    HANDLE TokenHandle
    )
//
// This routine turns on SeTakeOwnershipPrivilege in the current
// token.  Once that has been accomplished, we can open the file
// for WRITE_OWNER even if we are denied that access by the ACL
// on the file.

{
    LUID TakeOwnershipValue;
    BOOL Result;
    TOKEN_PRIVILEGES TokenPrivileges;

    //
    // First, find out the value of TakeOwnershipPrivilege
    //

    Result = LookupPrivilegeValue(
                 NULL,
                 "SeTakeOwnershipPrivilege",
                 &TakeOwnershipValue
                 );

    if ( !Result ) {

        //
        // This should not happen
        //

        printf("Unable to obtain value of TakeOwnership privilege\n");
        printf("Error = %d\n",GetLastError());
        printf("Exiting\n");
        return FALSE;
    }

    //
    // Set up the privilege set we will need
    //

    TokenPrivileges.PrivilegeCount = 1;
    TokenPrivileges.Privileges[0].Luid = TakeOwnershipValue;
    TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    (VOID) AdjustTokenPrivileges (
                TokenHandle,
                FALSE,
                &TokenPrivileges,
                sizeof( TOKEN_PRIVILEGES ),
                NULL,
                NULL
                );

    if ( GetLastError() != NO_ERROR ) {

        return( FALSE );

    } else {

        return( TRUE );
    }

}

BOOL
VariableInitialization()

//
// Create some useful SIDs.
//

{

    BOOL Result;

    Result = AllocateAndInitializeSid(
                 &SepNtAuthority,
                 2,
                 SECURITY_BUILTIN_DOMAIN_RID,
                 DOMAIN_ALIAS_RID_ADMINS,
                 0,
                 0,
                 0,
                 0,
                 0,
                 0,
                 &AliasAdminsSid
                 );

    if ( !Result ) {
        return( FALSE );
    }

    Result = AllocateAndInitializeSid(
                 &SepWorldSidAuthority,
                 1,
                 SECURITY_WORLD_RID,
                 0,
                 0,
                 0,
                 0,
                 0,
                 0,
                 0,
                 &SeWorldSid
                 );

    if ( !Result ) {
        return( FALSE );
    }

    return( TRUE );
}


TEXTSID.C

/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    textsid.c

Abstract:

    This module illustrates how to obtain the textual representation
    of a binary Sid.  This is useful in scenarios where it is not appropriate
    to obtain the name associated with the Sid, or, when the network is not
    available to obtain such information.  The Windows NT event viewer
    utility displays Sids in textual form when the username cannot be
    looked up.  Furthermore, lookup of local user profiles is facilitated
    through conversion of the user Sid to textual form.

    This sample obtains the Sid of the current user and then displays the
    Sid in textual notation.

    A standardized shorthand notation for SIDs makes it simpler to
    visualize their components:

    S-R-I-S-S...

    In the notation shown above,

    S identifies the series of digits as an SID,
    R is the revision level,
    I is the identifier-authority value,
    S is subauthority value(s).

    An SID could be written in this notation as follows:
    S-1-5-32-544

    In this example,
    the SID has a revision level of 1,
    an identifier-authority value of 5,
    first subauthority value of 32,
    second subauthority value of 544.
    (Note that the above Sid represents the local Administrators group)

    The GetTextualSid() function will convert a binary Sid to a textual
    string.

    The resulting string will take one of two forms.  If the
    IdentifierAuthority value is not greater than 2^32, then the SID
    will be in the form:

    S-1-5-21-2127521184-1604012920-1887927527-19009
      ^ ^ ^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^
      | | |      |          |          |        |
      +-+-+------+----------+----------+--------+--- Decimal

    Otherwise it will take the form:

    S-1-0x206C277C6666-21-2127521184-1604012920-1887927527-19009
      ^ ^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^
      |       |        |      |          |          |        |
      |   Hexidecimal  |      |          |          |        |
      +----------------+------+----------+----------+--------+--- Decimal

    If the function succeeds, the return value is TRUE.
    If the function fails, the return value is FALSE.  To get extended
        error information, call the Win32 API GetLastError().

Author:

    Scott Field (sfield)    13-Apr-96

Revision History:

    Scott Field (sfield)    11-Jul-95
    Unicode enabled

    Scott Field (sfield)    15-May-95
--*/

#define RTN_OK 0
#define RTN_ERROR 13

#include <windows.h>
#include <stdio.h>

BOOL
GetTextualSid(
    PSID pSid,          // binary Sid
    LPTSTR TextualSID,  // buffer for Textual representaion of Sid
    LPDWORD cchSidSize  // required/provided TextualSid buffersize
    );

void
DisplayWinError(
    LPSTR szAPI,    // pointer to Ansi function name
    DWORD dwError   // DWORD WinError
    );

int
__cdecl
main(
    void
    )
{
#define MY_BUFSIZE 256 // all allocations should be dynamic
    HANDLE hToken;
    BYTE buf[MY_BUFSIZE];
    PTOKEN_USER ptgUser = (PTOKEN_USER)buf;
    DWORD cbBuffer=MY_BUFSIZE;

    TCHAR szTextualSid[MY_BUFSIZE];
    DWORD cchSid=MY_BUFSIZE;

    BOOL bSuccess;

    //
    // obtain current process token
    //
    if(!OpenProcessToken(
                GetCurrentProcess(), // target current process
                TOKEN_QUERY,         // TOKEN_QUERY access
                &hToken              // resultant hToken
                ))
    {
        DisplayWinError("OpenProcessToken", GetLastError());
        return RTN_ERROR;
    }

    //
    // obtain user identified by current process' access token
    //
    bSuccess = GetTokenInformation(
                hToken,    // identifies access token
                TokenUser, // TokenUser info type
                ptgUser,   // retrieved info buffer
                cbBuffer,  // size of buffer passed-in
                &cbBuffer  // required buffer size
                );

    // close token handle.  do this even if error above
    CloseHandle(hToken);

    if(!bSuccess) {
        DisplayWinError("GetTokenInformation", GetLastError());
        return RTN_ERROR;
    }

    //
    // obtain the textual representaion of the Sid
    //
    if(!GetTextualSid(
                ptgUser->User.Sid, // user binary Sid
                szTextualSid,      // buffer for TextualSid
                &cchSid            // size/required buffer
                )) {
        DisplayWinError("GetTextualSid", GetLastError());
        return RTN_ERROR;
    }

    // display the TextualSid representation
    printf("%s\n", szTextualSid);

    return RTN_OK;
}

BOOL
GetTextualSid(
    PSID pSid,          // binary Sid
    LPTSTR TextualSid,  // buffer for Textual representaion of Sid
    LPDWORD cchSidSize  // required/provided TextualSid buffersize
    )
{
    PSID_IDENTIFIER_AUTHORITY psia;
    DWORD dwSubAuthorities;
    DWORD dwCounter;
    DWORD cchSidCopy;

    //
    // test if Sid passed in is valid
    //
    if(!IsValidSid(pSid)) return FALSE;

    // obtain SidIdentifierAuthority
    psia = GetSidIdentifierAuthority(pSid);

    // obtain sidsubauthority count
    dwSubAuthorities = *GetSidSubAuthorityCount(pSid);

    //
    // compute approximate buffer length
    // S-SID_REVISION- + identifierauthority- + subauthorities- + NULL
    //
    cchSidCopy = (15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);

    //
    // check provided buffer length.
    // If not large enough, indicate proper size and setlasterror
    //
    if(*cchSidSize < cchSidCopy) {
        *cchSidSize = cchSidCopy;
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    //
    // prepare S-SID_REVISION-
    //
    cchSidCopy = wsprintf(TextualSid, TEXT("S-%lu-"), SID_REVISION );

    //
    // prepare SidIdentifierAuthority
    //
    if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) ) {
        cchSidCopy += wsprintf(TextualSid + cchSidCopy,
                    TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
                    (USHORT)psia->Value[0],
                    (USHORT)psia->Value[1],
                    (USHORT)psia->Value[2],
                    (USHORT)psia->Value[3],
                    (USHORT)psia->Value[4],
                    (USHORT)psia->Value[5]);
    } else {
        cchSidCopy += wsprintf(TextualSid + cchSidCopy,
                    TEXT("%lu"),
                    (ULONG)(psia->Value[5]      )   +
                    (ULONG)(psia->Value[4] <<  8)   +
                    (ULONG)(psia->Value[3] << 16)   +
                    (ULONG)(psia->Value[2] << 24)   );
    }

    //
    // loop through SidSubAuthorities
    //
    for(dwCounter = 0 ; dwCounter < dwSubAuthorities ; dwCounter++) {
        cchSidCopy += wsprintf(TextualSid + cchSidCopy, TEXT("-%lu"),
                    *GetSidSubAuthority(pSid, dwCounter) );
    }

    //
    // tell the caller how many chars we provided, not including NULL
    //
    *cchSidSize = cchSidCopy;

    return TRUE;
}

void
DisplayWinError(
    LPSTR szAPI,    // pointer to Ansi function name
    DWORD dwError   // DWORD WinError
    )
{
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    //
    // TODO get this fprintf out of here!
    //
    fprintf(stderr,"%s error!\n", szAPI);

    if(dwBufferLength=FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM,
            NULL,
            dwError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPSTR) &MessageBuffer,
            0,
            NULL
            ))
    {
        DWORD dwBytesWritten; // unused

        //
        // Output message string on stderr
        //
        WriteFile(
                GetStdHandle(STD_ERROR_HANDLE),
                MessageBuffer,
                dwBufferLength,
                &dwBytesWritten,
                NULL
                );

        //
        // free the buffer allocated by the system
        //
        LocalFree(MessageBuffer);
    }
}


README.TXT

Multithreaded Heap Manager

Summary
-------

The MPHEAP sample is a DLL that provides an implementation of multiple heaps
and serializes access to the heaps.  It will only work on Windows NT 4.0 and
later.

Many multithreaded applications that use the standard memory allocation
routines pay a significant performance penalty when running on a
multiprocessor machine. This is due to the serialization used by the
default heap. On a multiprocessor machine, more than one thread may try to
allocate memory simultaneously. One thread will block on the critical section
guarding the heap. The other thread must then signal the critical section
when it is finished to release the waiting thread. This adds significant
overhead.

By providing multiple heaps, MPHEAP.DLL allows simultaneous operations on
each heap. A thread on processor 0 can allocate memory from one heap
at the same time that a thread on processor 1 is allocating from a
different heap. The additional overhead in this DLL is offset by
drastically reducing the number of times a thread must wait for heap access.

The TMPHEAP sample is a simple program that demonstrates the functionality of
MPHEAP.DLL.

More Information
----------------

The basic scheme is to attempt to lock each heap in turn with the
TryEnterCriticalSection function. This will enter the critical section if
it is unowned. If the critical section is owned by a different thread,
TryEnterCriticalSection returns failure instead of blocking until the
other thread leaves the critical section.

Another trick to increase performance is the use of a lookaside list to
satisfy frequent allocations. By using InterlockedExchange to remove
lookaside list entries and InterlockedCompareExchange to add lookaside
list entries, allocations and frees can be completed without needing a
critical section lock.

The final trick is the use of delayed frees. If a chunk of memory is
being freed, and the required lock is already held by a different
thread, the free block is simply added to a delayed free list and the
function completes immediately. The next thread to acquire the heap lock
will free everything on the list.

Every application uses memory allocation routines in different ways.
To allow better tuning, MpHeapGetStatistics allows an application to monitor
the contention. Increasing the number of heaps increases the potential
concurrency, but also increases memory overhead. Some experimentation
is recommended to determine the optimal settings for a given number of
processors.

Some applications can benefit from additional techniques. For example,
per-thread lookaside lists for common allocation sizes can be very
effective. No locking is required for a per-thread structure, since no
other thread will ever be accessing it. Because each thread reuses the
same memory, per-thread structures also improve locality of reference.


MPHEAP.C

/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    mpheap.c

Abstract:

    This DLL is a wrapper that sits on top of the Win32 Heap* api.  It
    provides multiple heaps and handles all the serialization itself.

    Many multithreaded applications that use the standard memory allocation
    routines (malloc/free, LocalAlloc/LocalFree, HeapAlloc/HeapFree) suffer
    a significant a significant performance penalty when running on a
    multi-processor machine.  This is due to the serialization used by the
    default heap package.  On a multiprocessor machine, more than one
    thread may simultaneously try to allocate memory.  One thread will
    block on the critical section guarding the heap.  The other thread must
    then signal the critical section when it is finished to unblock the
    waiting thread.  The additional codepath of blocking and signalling adds
    significant overhead to the frequent memory allocation path.

    By providing multiple heaps, this DLL allows simultaneous operations on
    each heap.  A thread on processor 0 can allocate memory from one heap
    at the same time that a thread on processor 1 is allocating from a
    different heap.  The additional overhead in this DLL is compensated by
    drastically reducing the number of times a thread must wait for heap
    access.

    The basic scheme is to attempt to lock each heap in turn with the new
    TryEnterCriticalSection API.  This will enter the critical section if
    it is unowned.  If the critical section is owned by a different thread,
    TryEnterCriticalSection returns failure instead of blocking until the
    other thread leaves the critical section.

    Another trick to increase performance is the use of a lookaside list to
    satisfy frequent allocations.  By using InterlockedExchange to remove
    lookaside list entries and InterlockedCompareExchange to add lookaside
    list entries, allocations and frees can be completed without needing a
    critical section lock.

    The final trick is the use of delayed frees.  If a chunk of memory is
    being freed, and the required lock is already held by a different
    thread, the free block is simply added to a delayed free list and the
    API completes immediately.  The next thread to acquire the heap lock
    will free everything on the list.

    Every application uses memory allocation routines in different ways.
    In order to allow better tuning of this package, MpHeapGetStatistics
    allows an application to monitor the amount of contention it is
    getting.  Increasing the number of heaps increases the potential
    concurrency, but also increases memory overhead.  Some experimentation
    is recommended to determine the optimal settings for a given number of
    processors.

    Some applications can benefit from additional techniques.  For example,
    per-thread lookaside lists for common allocation sizes can be very
    effective.  No locking is required for a per-thread structure, since no
    other thread will ever be accessing it.  Since each thread reuses the
    same memory, per-thread structures also improve locality of reference.

--*/
#include <windows.h>
#include "mpheap.h"

#define MPHEAP_VALID_OPTIONS  (MPHEAP_GROWABLE                 | \
                               MPHEAP_REALLOC_IN_PLACE_ONLY    | \
                               MPHEAP_TAIL_CHECKING_ENABLED    | \
                               MPHEAP_FREE_CHECKING_ENABLED    | \
                               MPHEAP_DISABLE_COALESCE_ON_FREE | \
                               MPHEAP_ZERO_MEMORY              | \
                               MPHEAP_COLLECT_STATS)

//
// Flags that are not passed on to the Win32 heap package
//
#define MPHEAP_PRIVATE_FLAGS (MPHEAP_COLLECT_STATS | MPHEAP_ZERO_MEMORY);

//
// Define the heap header that gets tacked on the front of
// every allocation. Eight bytes is a lot, but we can't make
// it any smaller or else the allocation will not be properly
// aligned for 64-bit quantities.
//
typedef struct _MP_HEADER {
    union {
        struct _MP_HEAP_ENTRY *HeapEntry;
        PSINGLE_LIST_ENTRY Next;
    };
    ULONG LookasideIndex;
} MP_HEADER, *PMP_HEADER;
//
// Definitions and structures for lookaside list
//
#define LIST_ENTRIES 128

typedef struct _MP_HEAP_LOOKASIDE {
    PMP_HEADER Entry;
} MP_HEAP_LOOKASIDE, *PMP_HEAP_LOOKASIDE;

#define NO_LOOKASIDE 0xffffffff
#define MaxLookasideSize (8*LIST_ENTRIES-7)
#define LookasideIndexFromSize(s) ((s < MaxLookasideSize) ? ((s) >> 3) : NO_LOOKASIDE)

//
// Define the structure that describes the entire MP heap.
//
// There is one MP_HEAP_ENTRY structure for each Win32 heap
// and a MP_HEAP structure that contains them all.
//
// Each MP_HEAP structure contains a lookaside list for quick
// lock-free alloc/free of various size blocks.
//

typedef struct _MP_HEAP_ENTRY {
    HANDLE Heap;
    PSINGLE_LIST_ENTRY DelayedFreeList;
    CRITICAL_SECTION Lock;
    DWORD Allocations;
    DWORD Frees;
    DWORD LookasideAllocations;
    DWORD LookasideFrees;
    DWORD DelayedFrees;
    MP_HEAP_LOOKASIDE Lookaside[LIST_ENTRIES];
} MP_HEAP_ENTRY, *PMP_HEAP_ENTRY;

typedef struct _MP_HEAP {
    DWORD HeapCount;
    DWORD Flags;
    DWORD Hint;
    DWORD PadTo32Bytes;
    MP_HEAP_ENTRY Entry[1];     // variable size
} MP_HEAP, *PMP_HEAP;

VOID
ProcessDelayedFreeList(
    IN PMP_HEAP_ENTRY HeapEntry
    );

//
// HeapHint is a per-thread variable that offers a hint as to which heap to
// check first.  By giving each thread affinity towards a different heap,
// it is more likely that the first heap a thread picks for its allocation
// will be available.  It also improves a thread's locality of reference,
// which is very important for good MP performance
//
__declspec(thread) DWORD HeapHint;

HANDLE
WINAPI
MpHeapCreate(
    DWORD flOptions,
    DWORD dwInitialSize,
    DWORD dwParallelism
    )
/*++

Routine Description:

    This routine creates an MP-enhanced heap. An MP heap consists of a
    collection of standard Win32 heaps whose serialization is controlled
    by the routines in this module to allow multiple simultaneous allocations.

Arguments:

    flOptions - Supplies the options for this heap.

        Currently valid flags are:

            MPHEAP_GROWABLE
            MPHEAP_REALLOC_IN_PLACE_ONLY
            MPHEAP_TAIL_CHECKING_ENABLED
            MPHEAP_FREE_CHECKING_ENABLED
            MPHEAP_DISABLE_COALESCE_ON_FREE
            MPHEAP_ZERO_MEMORY
            MPHEAP_COLLECT_STATS

    dwInitialSize - Supplies the initial size of the combined heaps.

    dwParallelism - Supplies the number of Win32 heaps that will make up the
        MP heap. A value of zero defaults to three + # of processors.

Return Value:

    HANDLE - Returns a handle to the MP heap that can be passed to the
             other routines in this package.

    NULL - Failure, GetLastError() specifies the exact error code.

--*/
{
    DWORD Error;
    DWORD i;
    HANDLE Heap;
    PMP_HEAP MpHeap;
    DWORD HeapSize;
    DWORD PrivateFlags;

    if (flOptions & ~MPHEAP_VALID_OPTIONS) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return(NULL);
    }

    flOptions |= HEAP_NO_SERIALIZE;

    PrivateFlags = flOptions & MPHEAP_PRIVATE_FLAGS;

    flOptions &= ~MPHEAP_PRIVATE_FLAGS;

    if (dwParallelism == 0) {
        SYSTEM_INFO SystemInfo;

        GetSystemInfo(&SystemInfo);
        dwParallelism = 3 + SystemInfo.dwNumberOfProcessors;
    }

    HeapSize = dwInitialSize / dwParallelism;

    //
    // The first heap is special, since the MP_HEAP structure itself
    // is allocated from there.
    //
    Heap = HeapCreate(flOptions,HeapSize,0);
    if (Heap == NULL) {
        //
        // HeapCreate has already set last error appropriately.
        //
        return(NULL);
    }

    MpHeap = HeapAlloc(Heap,0,sizeof(MP_HEAP) +
                              (dwParallelism-1)*sizeof(MP_HEAP_ENTRY));
    if (MpHeap==NULL) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        HeapDestroy(Heap);
        return(NULL);
    }

    //
    // Initialize the MP heap structure
    //
    MpHeap->HeapCount = 1;
    MpHeap->Flags = PrivateFlags;
    MpHeap->Hint = 0;

    //
    // Initialize the first heap
    //
    MpHeap->Entry[0].Heap = Heap;
    InitializeCriticalSection(&MpHeap->Entry[0].Lock);
    MpHeap->Entry[0].DelayedFreeList = NULL;
    ZeroMemory(MpHeap->Entry[0].Lookaside, sizeof(MpHeap->Entry[0].Lookaside));

    //
    // Initialize the remaining heaps. Note that the heap has been
    // sufficiently initialized to use MpHeapDestroy for cleanup
    // if something bad happens.
    //
    for (i=1; i<dwParallelism; i++) {
        MpHeap->Entry[i].Heap = HeapCreate(flOptions, HeapSize, 0);
        if (MpHeap->Entry[i].Heap == NULL) {
            Error = GetLastError();
            MpHeapDestroy((HANDLE)MpHeap);
            SetLastError(Error);
            return(NULL);
        }
        InitializeCriticalSection(&MpHeap->Entry[i].Lock);
        MpHeap->Entry[i].DelayedFreeList = NULL;
        ZeroMemory(MpHeap->Entry[i].Lookaside, sizeof(MpHeap->Entry[i].Lookaside));
        ++MpHeap->HeapCount;
    }

    return((HANDLE)MpHeap);
}

BOOL
WINAPI
MpHeapDestroy(
    HANDLE hMpHeap
    )
{
    DWORD i;
    DWORD HeapCount;
    PMP_HEAP MpHeap;
    BOOL Success = TRUE;

    MpHeap = (PMP_HEAP)hMpHeap;
    HeapCount = MpHeap->HeapCount;

    //
    // Lock down all the heaps so we don't end up hosing people
    // who may be allocating things while we are deleting the heaps.
    // By setting MpHeap->HeapCount = 0 we also attempt to prevent
    // people from getting hosed as soon as we delete the critical
    // sections and heaps.
    //
    MpHeap->HeapCount = 0;
    for (i=0; i<HeapCount; i++) {
        EnterCriticalSection(&MpHeap->Entry[i].Lock);
    }

    //
    // Delete the heaps and their associated critical sections.
    // Note that the order is important here. Since the MpHeap
    // structure was allocated from MpHeap->Heap[0] we must
    // delete that last.
    //
    for (i=HeapCount-1; i>0; i--) {
        DeleteCriticalSection(&MpHeap->Entry[i].Lock);
        if (!HeapDestroy(MpHeap->Entry[i].Heap)) {
            Success = FALSE;
        }
    }
    return(Success);
}

BOOL
WINAPI
MpHeapValidate(
    HANDLE hMpHeap,
    LPVOID lpMem
    )
{
    PMP_HEAP MpHeap;
    DWORD i;
    BOOL Success;
    PMP_HEADER Header;
    PMP_HEAP_ENTRY Entry;

    MpHeap = (PMP_HEAP)hMpHeap;

    if (lpMem == NULL) {

        //
        // Lock and validate each heap in turn.
        //
        for (i=0; i < MpHeap->HeapCount; i++) {
            Entry = &MpHeap->Entry[i];
            try {
                EnterCriticalSection(&Entry->Lock);
                Success = HeapValidate(Entry->Heap, 0, NULL);
                LeaveCriticalSection(&Entry->Lock);
            } except (EXCEPTION_EXECUTE_HANDLER) {
                return(FALSE);
            }

            if (!Success) {
                return(FALSE);
            }
        }
        return(TRUE);
    } else {

        //
        // Lock and validate the given heap entry
        //
        Header = ((PMP_HEADER)lpMem) - 1;
        try {
            EnterCriticalSection(&Header->HeapEntry->Lock);
            Success = HeapValidate(Header->HeapEntry->Heap, 0, Header);
            LeaveCriticalSection(&Header->HeapEntry->Lock);
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return(FALSE);
        }
        return(Success);
    }
}

UINT
WINAPI
MpHeapCompact(
    HANDLE hMpHeap
    )
{
    PMP_HEAP MpHeap;
    DWORD i;
    DWORD LargestFreeSize=0;
    DWORD FreeSize;
    PMP_HEAP_ENTRY Entry;

    MpHeap = (PMP_HEAP)hMpHeap;

    //
    // Lock and compact each heap in turn.
    //
    for (i=0; i < MpHeap->HeapCount; i++) {
        Entry = &MpHeap->Entry[i];
        EnterCriticalSection(&Entry->Lock);
        FreeSize = HeapCompact(Entry->Heap, 0);
        LeaveCriticalSection(&Entry->Lock);

        if (FreeSize > LargestFreeSize) {
            LargestFreeSize = FreeSize;
        }
    }

    return(LargestFreeSize);

}

LPVOID
WINAPI
MpHeapAlloc(
    HANDLE hMpHeap,
    DWORD flOptions,
    DWORD dwBytes
    )
{
    PMP_HEADER Header;
    PMP_HEAP MpHeap;
    DWORD i;
    PMP_HEAP_ENTRY Entry;
    DWORD Index;
    DWORD Size;

    MpHeap = (PMP_HEAP)hMpHeap;

    flOptions |= MpHeap->Flags;

    Size = ((dwBytes + 7) & (ULONG)~7) + sizeof(MP_HEADER);
    Index=LookasideIndexFromSize(Size);

    //
    // Iterate through the heap locks looking for one
    // that is not owned.
    //
    i=HeapHint;
    if (i>=MpHeap->HeapCount) {
        i=0;
        HeapHint=0;
    }
    Entry = &MpHeap->Entry[i];
    do {
        //
        // Check the lookaside list for a suitable allocation.
        //
        if ((Index != NO_LOOKASIDE) &&
            (Entry->Lookaside[Index].Entry != NULL)) {
            if ((Header = (PMP_HEADER)InterlockedExchange((PLONG)&Entry->Lookaside[Index].Entry,
                                                          (LONG)NULL)) != NULL) {
                //
                // We have a lookaside hit, return it immediately.
                //
                ++Entry->LookasideAllocations;
                if (flOptions & MPHEAP_ZERO_MEMORY) {
                    ZeroMemory(Header + 1, dwBytes);
                }
                HeapHint=i;
                return(Header + 1);
            }
        }

        //
        // Attempt to lock this heap without blocking.
        //
        if (TryEnterCriticalSection(&Entry->Lock)) {
            //
            // success, go allocate immediately
            //
            goto LockAcquired;
        }

        //
        // This heap is owned by another thread, try
        // the next one.
        //
        i++;
        Entry++;
        if (i==MpHeap->HeapCount) {
            i=0;
            Entry=&MpHeap->Entry[0];
        }
    } while ( i != HeapHint );

    //
    // All of the critical sections were owned by someone else,
    // so we have no choice but to wait for a critical section.
    //
    EnterCriticalSection(&Entry->Lock);

LockAcquired:
    ++Entry->Allocations;
    if (Entry->DelayedFreeList != NULL) {
        ProcessDelayedFreeList(Entry);
    }
    Header = HeapAlloc(Entry->Heap, 0, Size);
    LeaveCriticalSection(&Entry->Lock);
    if (Header != NULL) {
        Header->HeapEntry = Entry;
        Header->LookasideIndex = Index;
        if (flOptions & MPHEAP_ZERO_MEMORY) {
            ZeroMemory(Header + 1, dwBytes);
        }
        HeapHint = i;
        return(Header + 1);
    } else {
        return(NULL);
    }
}

LPVOID
WINAPI
MpHeapReAlloc(
    HANDLE hMpHeap,
    LPVOID lpMem,
    DWORD dwBytes
    )
{
    PMP_HEADER Header;
    PCRITICAL_SECTION Lock;

    Header = ((PMP_HEADER)lpMem) - 1;
    Lock = &Header->HeapEntry->Lock;
    dwBytes = ((dwBytes + 7) & (ULONG)~7) + sizeof(MP_HEADER);

    EnterCriticalSection(Lock);
    Header = HeapReAlloc(Header->HeapEntry->Heap, 0, Header, dwBytes);
    LeaveCriticalSection(Lock);

    if (Header != NULL) {
        Header->LookasideIndex = LookasideIndexFromSize(dwBytes);
        return(Header + 1);
    } else {
        return(NULL);
    }
}

BOOL
WINAPI
MpHeapFree(
    HANDLE hMpHeap,
    LPVOID lpMem
    )
{
    PMP_HEADER Header;
    PCRITICAL_SECTION Lock;
    BOOL Success;
    PMP_HEAP_ENTRY HeapEntry;
    PSINGLE_LIST_ENTRY Next;
    PMP_HEAP MpHeap;

    Header = ((PMP_HEADER)lpMem) - 1;
    HeapEntry = Header->HeapEntry;
    MpHeap = (PMP_HEAP)hMpHeap;

    HeapHint = HeapEntry - &MpHeap->Entry[0];

    if (Header->LookasideIndex != NO_LOOKASIDE) {
        //
        // Try and put this back on the lookaside list
        //
        if (InterlockedCompareExchange((PVOID *)&HeapEntry->Lookaside[Header->LookasideIndex],
                                       (PVOID)Header,
                                       NULL) == NULL) {
            //
            // Successfully freed to lookaside list.
            //
            ++HeapEntry->LookasideFrees;
            return(TRUE);
        }
    }
    Lock = &HeapEntry->Lock;

    if (TryEnterCriticalSection(Lock)) {
        ++HeapEntry->Frees;
        Success = HeapFree(HeapEntry->Heap, 0, Header);
        LeaveCriticalSection(Lock);
        return(Success);
    }
    //
    // The necessary heap critical section could not be immediately
    // acquired. Post this free onto the Delayed free list and let
    // whoever has the lock process it.
    //
    do {
        Next = HeapEntry->DelayedFreeList;
        Header->Next = Next;
    } while ( InterlockedCompareExchange(&HeapEntry->DelayedFreeList,
                                         &Header->Next,
                                         Next) != Next);
    return(TRUE);
}

VOID
ProcessDelayedFreeList(
    IN PMP_HEAP_ENTRY HeapEntry
    )
{
    PSINGLE_LIST_ENTRY FreeList;
    PSINGLE_LIST_ENTRY Next;
    PMP_HEADER Header;

    //
    // Capture the entire delayed free list with a single interlocked exchange.
    // Once we have removed the entire list, free each entry in turn.
    //
    FreeList = (PSINGLE_LIST_ENTRY)InterlockedExchange((LPLONG)&HeapEntry->DelayedFreeList, (LONG)NULL);
    while (FreeList != NULL) {
        Next = FreeList->Next;
        Header = CONTAINING_RECORD(FreeList, MP_HEADER, Next);
        ++HeapEntry->DelayedFrees;
        HeapFree(HeapEntry->Heap, 0, Header);
        FreeList = Next;
    }
}

DWORD
MpHeapGetStatistics(
    HANDLE hMpHeap,
    LPDWORD lpdwSize,
    MPHEAP_STATISTICS Stats[]
    )
{
    PMP_HEAP MpHeap;
    PMP_HEAP_ENTRY Entry;
    DWORD i;
    DWORD RequiredSize;

    MpHeap = (PMP_HEAP)hMpHeap;
    RequiredSize = MpHeap->HeapCount * sizeof(MPHEAP_STATISTICS);
    if (*lpdwSize < RequiredSize) {
        *lpdwSize = RequiredSize;
        return(ERROR_MORE_DATA);
    }
    ZeroMemory(Stats, MpHeap->HeapCount * sizeof(MPHEAP_STATISTICS));
    for (i=0; i < MpHeap->HeapCount; i++) {
        Entry = &MpHeap->Entry[i];

        Stats[i].Contention = Entry->Lock.DebugInfo->ContentionCount;
        Stats[i].TotalAllocates = (Entry->Allocations + Entry->LookasideAllocations);
        Stats[i].TotalFrees = (Entry->Frees + Entry->LookasideFrees + Entry->DelayedFrees);
        Stats[i].LookasideAllocates = Entry->LookasideAllocations;
        Stats[i].LookasideFrees = Entry->LookasideFrees;
        Stats[i].DelayedFrees = Entry->DelayedFrees;
    }
    *lpdwSize = RequiredSize;
    return(ERROR_SUCCESS);
}


MPHEAP.H

/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    mpheap.h

Abstract:

    Public header file for MP heap package

Author:

    John Vert (jvert) 10-Jul-1995

Revision History:

--*/

//
// Valid heap creation options
//
#define MPHEAP_GROWABLE HEAP_GROWABLE
#define MPHEAP_REALLOC_IN_PLACE_ONLY HEAP_REALLOC_IN_PLACE_ONLY
#define MPHEAP_TAIL_CHECKING_ENABLED HEAP_TAIL_CHECKING_ENABLED
#define MPHEAP_FREE_CHECKING_ENABLED HEAP_FREE_CHECKING_ENABLED
#define MPHEAP_DISABLE_COALESCE_ON_FREE HEAP_DISABLE_COALESCE_ON_FREE
#define MPHEAP_ZERO_MEMORY HEAP_ZERO_MEMORY
#define MPHEAP_COLLECT_STATS 0x10000000

HANDLE
WINAPI
MpHeapCreate(
    DWORD flOptions,
    DWORD dwInitialSize,
    DWORD dwParallelism
    );

BOOL
WINAPI
MpHeapDestroy(
    HANDLE hMpHeap
    );

BOOL
WINAPI
MpHeapValidate(
    HANDLE hMpHeap,
    LPVOID lpMem
    );

UINT
WINAPI
MpHeapCompact(
    HANDLE hMpHeap
    );

LPVOID
WINAPI
MpHeapAlloc(
    HANDLE hMpHeap,
    DWORD flOptions,
    DWORD dwBytes
    );

LPVOID
WINAPI
MpHeapReAlloc(
    HANDLE hMpHeap,
    LPVOID lpMem,
    DWORD dwBytes
    );

BOOL
WINAPI
MpHeapFree(
    HANDLE hMpHeap,
    LPVOID lpMem
    );

//
// Statistics structure
//
typedef struct _MPHEAP_STATISTICS {
    DWORD Contention;
    DWORD TotalAllocates;
    DWORD TotalFrees;
    DWORD LookasideAllocates;
    DWORD LookasideFrees;
    DWORD DelayedFrees;
} MPHEAP_STATISTICS, *LPMPHEAP_STATISTICS;

DWORD
MpHeapGetStatistics(
    HANDLE hMpHeap,
    LPDWORD lpdwSize,
    MPHEAP_STATISTICS Statistics[]
    );


TMPHEAP.C

/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    tmpheap.c

Abstract:

    Test program for the MP heap package.

Author:

    John Vert (jvert) 10-Jul-1995

Revision History:

--*/
#include "windows.h"
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "mpheap.h"

DWORD
WINAPI
ThreadStartup(
    LPVOID Parameter
    );

LPVOID Malloc(DWORD dwBytes);
VOID Free(LPVOID lpMem);

BOOL Suicide = FALSE;

HANDLE MpHeap;

LPVOID
Malloc(
    DWORD dwBytes
    )
{
    return(MpHeapAlloc(MpHeap,0,dwBytes));
}

VOID
Free(
    LPVOID lpMem
    )
{
    MpHeapFree(MpHeap,lpMem);
}

// int
// _CRTAPI1
main (argc, argv)
    int argc;
    char *argv[];
{
    ULONG NumThreads;
    ULONG i;
    HANDLE *h;
    DWORD ThreadId;
    DWORD Parallelism;
    DWORD Seconds;
    LPMPHEAP_STATISTICS HeapStats;
    DWORD StatSize;
    DWORD Error;

    if (argc != 4) {
        fprintf(stderr, "Usage: tmpheap NumThreads HeapParallelism Seconds\n");
        exit(1);
    }
    NumThreads = atoi(argv[1]);
    Parallelism = atoi(argv[2]);
    Seconds = atoi(argv[3]);

    MpHeap = MpHeapCreate(0, 0, Parallelism);
    if (MpHeap == NULL) {
        fprintf(stderr, "MpHeapCreate failed error %d\n",GetLastError);
        exit(1);
    }
    h = Malloc(NumThreads * sizeof(HANDLE));
    if (h==NULL) {
        fprintf(stderr, "Malloc thread handle array failed\n");
        exit(1);
    }
    for (i=0; i<NumThreads; i++) {
        h[i] = CreateThread(NULL,
                            0,
                            ThreadStartup,
                            NULL,
                            0,
                            &ThreadId);
        if (h==NULL) {
            fprintf(stderr, "CreateThread %d failed %d\n",i,GetLastError());
            exit(1);
        }
    }

    Sleep(Seconds * 1000);
    Suicide = TRUE;
    WaitForMultipleObjects(NumThreads, h, TRUE, INFINITE);
    StatSize = Parallelism * sizeof(MPHEAP_STATISTICS);
    HeapStats = (LPMPHEAP_STATISTICS)LocalAlloc(LMEM_FIXED, StatSize);
    if (HeapStats != NULL) {
        Error = MpHeapGetStatistics(MpHeap,&StatSize, HeapStats);
        if (Error==ERROR_SUCCESS) {
            for (i=0; i<StatSize/sizeof(MPHEAP_STATISTICS);i++) {
                printf("HEAP %d\n",i);
                printf("  Allocations:      %8d\n",HeapStats[i].TotalAllocates);
                if (0 == HeapStats[i].TotalAllocates) continue;  // avoid divide by 0
                printf("  Contention :      %8d (%d%%)\n",HeapStats[i].Contention,
                        100*HeapStats[i].Contention/HeapStats[i].TotalAllocates);
                printf("  Lookaside Allocs: %8d (%d%%)\n",
                        HeapStats[i].LookasideAllocates,
                        100*HeapStats[i].LookasideAllocates/HeapStats[i].TotalAllocates);
                printf("  Frees:            %8d\n",HeapStats[i].TotalFrees);
                printf("  Lookaside Frees:  %8d (%d%%)\n",
                        HeapStats[i].LookasideFrees,
                        100*HeapStats[i].LookasideFrees/HeapStats[i].TotalFrees);
                printf("  Delayed Frees:    %8d (%d%%)\n",
                        HeapStats[i].DelayedFrees,
                        100*HeapStats[i].DelayedFrees/HeapStats[i].TotalFrees);
            }
        } else {
            fprintf(stderr, "MpHeapStatistics failed with error %d\n",Error);
        }
        MpHeapDestroy(MpHeap);
        LocalFree(HeapStats);
    }

    return(0);
}

DWORD
WINAPI
ThreadStartup(
    LPVOID Parameter
    )
{
    DWORD Seed = GetCurrentThreadId();
    PULONG Buffer = NULL;
    DWORD BufferSize;
    DWORD Delay;
    DWORD i,j;
    DWORD Fill;
    PULONG Last = NULL;
    BOOL AllocAgain;
    DWORD MaxSize=0;
    DWORD CurrentSize=0;
    DWORD CurrentAllocs = 0;
    DWORD MaxAllocs = 0;

    //
    // Loop allocating/filling/freeing random chunks
    // of memory.
    //
    Fill = GetCurrentThreadId();
    while (!Suicide) {
        //
        // Decide whether to allocate a new chunk or free the
        // last chunk.
        //
        AllocAgain = (BOOL)((rand() & 0xff) > 0x80);
        if (AllocAgain) {
            BufferSize = (rand() & 0xfff) + sizeof(PUCHAR) + sizeof(ULONG);

            CurrentAllocs++;
            if (CurrentAllocs > MaxAllocs) {
                MaxAllocs = CurrentAllocs;
            }
            Buffer = Malloc(BufferSize);
            if (Buffer == NULL) {
                fprintf(stderr,"malloc of %d returned NULL\n",BufferSize);
                return(0);
            }
            CurrentSize += BufferSize;
            if (CurrentSize > MaxSize) {
                if ((CurrentSize >> 18) != (MaxSize >> 18)) {
                    printf("Thread %x up to %d bytes %d allocs\n",
                            GetCurrentThreadId(),
                            CurrentSize,
                            CurrentAllocs);
                }
                MaxSize = CurrentSize;
            }
            if ((CurrentSize > MaxSize) &&
                (CurrentSize-MaxSize > 0x10000)) {
            }
            for (j=0; j<BufferSize/sizeof(ULONG); j++) {
                Buffer[j] = Fill;
            }
            Buffer[0] = (ULONG)Last;
            Buffer[1] = BufferSize;
            Last = Buffer;
        } else if (Buffer != NULL) {
            Last = (PULONG)Buffer[0];
            CurrentSize -= Buffer[1];
            --CurrentAllocs;
            Free(Buffer);
            Buffer = Last;
        }

        Delay = rand() & 0x1ff;
        for (i=0; i< Delay; i++) {
            Fill = GetCurrentThreadId();
        }
        if ((rand() & 0xffff) < (int)CurrentAllocs) {
            //
            // Magic number, free EVERYTHING!
            //
            while (Buffer != NULL) {
                Last = (PULONG)Buffer[0];
                CurrentSize -= Buffer[1];
                --CurrentAllocs;
                Free(Buffer);
                Buffer = Last;
            }
        }
    }
    return(0);
}


PROCLIST.C

/*
    proclist.c

    sample program to list current processes using the PDH.DLL
    browsing functions

*/
#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE            1
#endif
#define tmain   wmain
#else
#define tmain   main
#endif

// this program only needs the "bare essential" windows header files
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <winperf.h>
#include <malloc.h>
#include <stdio.h>
#include <tchar.h>
#include <pdh.h>

int
tmain ()
{
    PDH_STATUS  pdhStatus               = ERROR_SUCCESS;
    LPTSTR      szCounterListBuffer     = NULL;
    DWORD       dwCounterListSize       = 0;
    LPTSTR      szInstanceListBuffer    = NULL;
    DWORD       dwInstanceListSize      = 0;
    LPTSTR      szThisInstance          = NULL;

    // call the function to determine the required buffer size for the data
    pdhStatus = PdhEnumObjectItems (
        NULL,                   // reserved
        NULL,                   // local machine
        TEXT("Process"),        // object to enumerate
        szCounterListBuffer,    // pass in NULL buffers
        &dwCounterListSize,     // an 0 length to get
        szInstanceListBuffer,   // required size
        &dwInstanceListSize,    // of the buffers in chars
        PERF_DETAIL_WIZARD,     // counter detail level
        0);

    if (pdhStatus == ERROR_SUCCESS) {
        // allocate the buffers and try the call again
        szCounterListBuffer = (LPTSTR)malloc (
            (dwCounterListSize * sizeof (TCHAR)));
        szInstanceListBuffer = (LPTSTR)malloc (
            (dwInstanceListSize * sizeof (TCHAR)));
        if ((szCounterListBuffer != NULL) &&
            (szInstanceListBuffer != NULL)) {
            pdhStatus = PdhEnumObjectItems (
                NULL,   // reserved
                NULL,   // local machine
                TEXT("Process"), // object to enumerate
                szCounterListBuffer,    // pass in NULL buffers
                &dwCounterListSize,     // an 0 length to get
                szInstanceListBuffer,   // required size
                &dwInstanceListSize,    // of the buffers in chars
                PERF_DETAIL_WIZARD,     // counter detail level
                0);
            if (pdhStatus == ERROR_SUCCESS) {
                _tprintf (TEXT("\nRunning Processes:"));
                // walk the return instance list
                for (szThisInstance = szInstanceListBuffer;
                     *szThisInstance != 0;
                     szThisInstance += lstrlen(szThisInstance) + 1) {
                     _tprintf (TEXT("\n  %s"), szThisInstance);
                }
            }
        } else {
            _tprintf (TEXT("\nPROCLIST: unable to allocate buffers"));
        }
        if (szCounterListBuffer != NULL) free (szCounterListBuffer);
        if (szInstanceListBuffer != NULL) free (szInstanceListBuffer);
    } else {
        _tprintf (TEXT("\nPROCLIST: unable to determine the necessary buffer size required"));
    }
    return 0;
}


README.TXT

PDH Counter Statistics Demo Application

The StatList application demonstrates use of the Counter Browser and
statistical counter data functions provided by the PDH library.

The files included in this sample are:

STATLIST.C - The .C source file
STATLIST.H - The .H file for the sample
RESOURCE.H - The .H file created by VC++ 4.0
STATLIST.DEF - The definition file
STATLIST.RC - Contains resource information such as menus and dialog boxes
STATLIST.HPJ - The Help file source
STATLIST.CNT - The contents list for the Help file
STATLIST.RTF -  A rich text format file used for Help
STATLIST.MAK - A Microsoft Visual C++ 4.0 make file
MAKEFILE - A command line make file
MAKEHELP.BAT - A batch file that makes the Help file
STATLIST.ICO - The icon for the STATLIST sample
SMALL.ICO - The small-icon for the STATLIST sample
STATLIST.BMP - A bitmap for the STATLIST sample
HELP.BMP - A bitmap for the Help file


ABOUTDLG.H

// global functions found in AboutDlg.c
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);


MAINWND.C

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1996 - 1997 Microsoft Corporation.  All Rights Reserved.
//
// Mainwnd: mainwnd.c
//
// PURPOSE: main window functions
//
// PLATFORMS:  Windows NT (only)
//
// FUNCTIONS:
//  DeleteAllCounters()     clears counters from query
//  On_WM_CREATE()          processes WM_CREATE message
//  On_IDM_GET_DATA()       gets the current perf data and updates window
//  On_IDM_ADD_COUNTERS()   displays browser & adds perf. counter to display
//  On_IDM_CLEAR_ALL()      clear all counters & refresh display
//  On_WM_COMMAND()         processes WM_COMMAND messages
//  On_WM_RBUTTONDOWN()     processes Right Mouse button clicks
//  On_WM_PAINT()           draws the current data in the main window
//  On_WM_DESTROY()         closes any open interfaces & memory allocations
//  WndProc()               main window procedure for this app's window
//
// SPECIAL INSTRUCTIONS: N/A
//
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <winperf.h>
#include <stdio.h>
#include <pdh.h>
#include <pdhmsg.h>

#include "statlist.h"
#include "winutils.h"
#include "mainwnd.h"
#include "aboutdlg.h"

// font for text in window
static   HFONT hFinePrint = NULL;

// PDH Query Handle for these counters
static HQUERY   hQuery = NULL;

// pointer to first item in counter list
static PCIB    pFirstCib = NULL;

int nTabStops[] = {300, 400, 500, 600, 700};
int nNumTabStops = sizeof(nTabStops) / sizeof(int);

#define NUM_STAT_SAMPLES    100

static
void
DeleteAllCounters ()
{
    PCIB   pCib, pCibOld;

    // close PDH Query
    // this removes all counters from the query as well as
    // removes the query item itself
    if (hQuery != NULL) {
        PdhCloseQuery (hQuery);
        hQuery = NULL;
    }

    // clean up any memory allocations
    pCib = pFirstCib;
    while ( pCib != NULL) {
        pCibOld = pCib;
        if (pCibOld->pCounterArray != NULL) {
            HeapFree (GetProcessHeap(), 0, pCibOld->pCounterArray);
        }
        pCib = pCib->pNext;
        HeapFree (GetProcessHeap(), 0, pCibOld);
    }
    pFirstCib = NULL;
}

// windows message functions
static
LRESULT
On_WM_CREATE (
    HWND hWnd,
    WPARAM wParam,
    LPARAM lParam)
{
    PDH_STATUS  pdhStatus;

    if (hQuery == NULL) {
        pdhStatus = PdhOpenQuery (NULL, 0, &hQuery);
    }
    if (hFinePrint == NULL) {
        hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                    VARIABLE_PITCH | FF_SWISS, "");
    }
    return ERROR_SUCCESS;
}

static
LRESULT
On_IDM_GET_DATA (
    HWND hWnd,
    WPARAM wParam,
    LPARAM lParam)
{
    PDH_STATUS  pdhStatus;
    DWORD       dwType;
    PCIB        pCib;
    PDH_FMT_COUNTERVALUE    pValue;
    PDH_RAW_COUNTER         pRaw;

    if (hQuery != NULL) {
        // get the current values of the query data
        pdhStatus = PdhCollectQueryData (hQuery);
        if (pdhStatus == ERROR_SUCCESS) {
            // loop through all counters and update the display values
            // and statistics
            for (pCib = pFirstCib; pCib != NULL; pCib = pCib->pNext) {
                // update "Last value"
                pdhStatus = PdhGetFormattedCounterValue (
                    pCib->hCounter, PDH_FMT_DOUBLE, &dwType, &pValue);
                pCib->dLastValue = pValue.doubleValue;
                // update "Raw Value" and statistics
                pdhStatus = PdhGetRawCounterValue (
                    pCib->hCounter, &dwType, &pRaw);
                pCib->pCounterArray[pCib->dwNextIndex] = pRaw;
                pdhStatus = PdhComputeCounterStatistics (
                    pCib->hCounter,
                    PDH_FMT_DOUBLE,
                    pCib->dwFirstIndex,
                    ++pCib->dwLastIndex,
                    pCib->pCounterArray,
                    &pCib->pdhCurrentStats);
                // update pointers & indeces
                if (pCib->dwLastIndex < NUM_STAT_SAMPLES) {
                    pCib->dwNextIndex = ++pCib->dwNextIndex % NUM_STAT_SAMPLES;
                } else {
                    --pCib->dwLastIndex;
                    pCib->dwNextIndex = pCib->dwFirstIndex;
                    pCib->dwFirstIndex = ++pCib->dwFirstIndex % NUM_STAT_SAMPLES;
                }
            }
            // cause the window to be repainted with the new values
            // (NOTE: This isn't the most efficient method of
            // display updating.)
            InvalidateRect (hWnd, NULL, TRUE);
        }
    }
    return ERROR_SUCCESS;
}

static
LRESULT
On_IDM_ADD_COUNTERS (
    HWND hWnd,
    WPARAM wParam,
    LPARAM lParam)
{
    PDH_BROWSE_DLG_CONFIG  BrowseInfo;
    CHAR            szCounterBuffer[MAX_PATH];
    PDH_STATUS      pdhStatus;
    PCIB            pNewCib;

    BrowseInfo.bIncludeInstanceIndex = FALSE;
    BrowseInfo.bSingleCounterPerAdd = TRUE;
    BrowseInfo.bSingleCounterPerDialog = TRUE;
    BrowseInfo.bLocalCountersOnly = FALSE;
    BrowseInfo.bWildCardInstances = FALSE;
    BrowseInfo.bHideDetailBox = FALSE;
    BrowseInfo.bDisableMachineSelection = FALSE;
    BrowseInfo.bInitializePath = FALSE;
    BrowseInfo.bIncludeCostlyObjects = TRUE;
    BrowseInfo.hWndOwner = hWnd;
    BrowseInfo.szReturnPathBuffer = szCounterBuffer;
    BrowseInfo.cchReturnPathLength = MAX_PATH;
    BrowseInfo.pCallBack = NULL;
    BrowseInfo.dwCallBackArg = 0;
    BrowseInfo.CallBackStatus = ERROR_SUCCESS;
    BrowseInfo.dwDefaultDetailLevel = PERF_DETAIL_WIZARD;
    BrowseInfo.szDialogBoxCaption = "Select a counter to monitor";

    pdhStatus = PdhBrowseCounters (&BrowseInfo);

    if (pdhStatus == ERROR_SUCCESS) {
        // add counter to the list
        pNewCib = (PCIB) HeapAlloc(GetProcessHeap(),
            HEAP_ZERO_MEMORY, sizeof(CIB));
        if (pNewCib != NULL) {
            // try to add the counter to the query
            pdhStatus = PdhAddCounter (hQuery,
                szCounterBuffer, 0, &pNewCib->hCounter);
            if (pdhStatus == ERROR_SUCCESS) {
                lstrcpy(pNewCib->szCounterPath, szCounterBuffer);
                // allocate the raw data buffer here
                pNewCib->pCounterArray = (PPDH_RAW_COUNTER)HeapAlloc (
                    GetProcessHeap(), HEAP_ZERO_MEMORY,
                    (sizeof(PDH_RAW_COUNTER) * NUM_STAT_SAMPLES));
                pNewCib->dwFirstIndex = 0;
                pNewCib->dwNextIndex = 0;
                pNewCib->dwLastIndex = 0;
                pNewCib->dLastValue = 0.0f;
                pNewCib->pdhCurrentStats.dwFormat = 0;
                pNewCib->pdhCurrentStats.count = 0;
                pNewCib->pdhCurrentStats.min.CStatus = PDH_CSTATUS_INVALID_DATA;
                pNewCib->pdhCurrentStats.min.largeValue = 0;
                pNewCib->pdhCurrentStats.max.CStatus = PDH_CSTATUS_INVALID_DATA;
                pNewCib->pdhCurrentStats.max.largeValue = 0;
                pNewCib->pdhCurrentStats.mean.CStatus = PDH_CSTATUS_INVALID_DATA;
                pNewCib->pdhCurrentStats.mean.largeValue = 0;

                //add to the top of the list
                pNewCib->pNext = pFirstCib;
                pFirstCib = pNewCib;

                // repaint window to get new entry
                InvalidateRect (hWnd, NULL, TRUE);
            } // else unable to add counter
        } // else unable to allocate pointer
    } // else user cancelled
    return ERROR_SUCCESS;
}

static
LRESULT
On_IDM_CLEAR_ALL (
    HWND hWnd,
    WPARAM wParam,
    LPARAM lParam)
{
    PDH_STATUS  pdhStatus;

    // delete all current counters, then create a new query
    DeleteAllCounters();

    pdhStatus = PdhOpenQuery (NULL, 0, &hQuery);

    InvalidateRect (hWnd, NULL, TRUE);

    return ERROR_SUCCESS;
}

static
LRESULT
On_WM_COMMAND (
    HWND hWnd,
    WPARAM wParam,
    LPARAM lParam)
{
    int wmId, wmEvent;
    char szHelpFileName[MAX_PATH];
    BOOL bGotHelp;

    wmId    = LOWORD(wParam); // Remember, these are...
    wmEvent = HIWORD(wParam); // ...different for Win32!

    //Parse the menu selections:
    switch (wmId) {
         // File Menu Item
        case IDM_NEW:
        case IDM_OPEN:
        case IDM_SAVE:
        case IDM_SAVEAS:
           return (DefWindowProc(hWnd, WM_COMMAND, wParam, lParam));

        case IDM_EXIT:
           DestroyWindow (hWnd);
           return ERROR_SUCCESS;

        // configure menu item
        case IDM_ADD_COUNTERS:
            return On_IDM_ADD_COUNTERS (hWnd, wParam, lParam);

        case IDM_CLEAR_ALL:
            return On_IDM_CLEAR_ALL (hWnd, wParam, lParam);

        // get data menu item
        case IDM_GET_DATA:
           return On_IDM_GET_DATA (hWnd, wParam, lParam);

        // Help Menu Item
        case IDM_HELPTOPICS:
           lstrcpy (szHelpFileName, APPNAME);
           lstrcat (szHelpFileName, ".HLP");
           bGotHelp = WinHelp (hWnd, szHelpFileName, HELP_FINDER,(DWORD)0);
           if (!bGotHelp)
           {
              MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP),
                          APPNAME, MB_OK|MB_ICONHAND);
           }
           return ERROR_SUCCESS;

        case IDM_ABOUT:
           DialogBox((HINSTANCE)(GetWindowLong(hWnd, GWL_HINSTANCE)),
               "AboutBox", hWnd, (DLGPROC)About);
           return ERROR_SUCCESS;

        default:
           return (DefWindowProc(hWnd, WM_COMMAND, wParam, lParam));
    }
}

static LRESULT
On_WM_RBUTTONDOWN (
    HWND hWnd,
    WPARAM wParam,
    LPARAM lParam)
{
    POINT pnt;
    HMENU hMenu;

    pnt.x = LOWORD(lParam);
    pnt.y = HIWORD(lParam);
    ClientToScreen(hWnd, (LPPOINT) &pnt);
    // This is where you would determine the appropriate 'context'
    // menu to bring up. Since this app has no real functionality,
    // we will just bring up the 'configure' menu:
    hMenu = GetSubMenu (GetMenu (hWnd), 1);
    if (hMenu) {
        TrackPopupMenu (hMenu, 0, pnt.x, pnt.y, 0, hWnd, NULL);
    } else {
    // Couldn't find the menu...
        MessageBeep(0);
    }

    return ERROR_SUCCESS;
}

static LRESULT
On_WM_PAINT (
    HWND hWnd,
    WPARAM wParam,
    LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    PCIB    pCib;
    int     nX, nY;
    LONG    lTextOutReturn;
    char    szOutputString[MAX_PATH];
    int     nStringLength;

    hdc = BeginPaint (hWnd, &ps);

    nX = 0;
    nY = 0;
    // draw Title text
    lstrcpy (szOutputString, "Performance Counter\tLast Value\tMinimum\tMaximum\tAverage");
    nStringLength = lstrlen(szOutputString) * sizeof(TCHAR);
    lTextOutReturn = TabbedTextOut (hdc, nX, nY,
        szOutputString, nStringLength,
        nNumTabStops, nTabStops, 0);
    nY += HIWORD(lTextOutReturn);

    // select the fine print font for this window
    SelectObject(hdc, hFinePrint);

    // for each CIB in the list draw the current text and value
    for (pCib = pFirstCib; pCib != NULL; pCib = pCib->pNext) {
        nStringLength = sprintf (szOutputString, "%s\t%f\t%f\t%f\t%f",
            pCib->szCounterPath, pCib->dLastValue,
            pCib->pdhCurrentStats.min.doubleValue,
            pCib->pdhCurrentStats.max.doubleValue,
            pCib->pdhCurrentStats.mean.doubleValue);
        lTextOutReturn = TabbedTextOut (hdc, nX, nY,
            szOutputString, nStringLength,
            nNumTabStops, nTabStops, 0);
        nY += HIWORD(lTextOutReturn);
    }

    EndPaint (hWnd, &ps);
    return ERROR_SUCCESS;
}

static LRESULT
On_WM_DESTROY (
    HWND hWnd,
    WPARAM wParam,
    LPARAM lParam)
{
     char szHelpFileName[MAX_PATH];

     lstrcpy (szHelpFileName, APPNAME);
     lstrcat (szHelpFileName, ".HLP");
     // Tell WinHelp we don't need it any more...
     WinHelp (hWnd, szHelpFileName, HELP_QUIT,(DWORD)0);

     DeleteAllCounters();

     PostQuitMessage(0);
     return ERROR_SUCCESS;
}

//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  MESSAGES:
//
//    WM_COMMAND - process the application menu
//    WM_PAINT - Paint the main window
//    WM_DESTROY - post a quit message and return
//    WM_RBUTTONDOWN - Right mouse click -- put up context menu here if appropriate
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
        case WM_CREATE:
            return On_WM_CREATE (hWnd, wParam, lParam);

        case WM_COMMAND:
            return On_WM_COMMAND (hWnd, wParam, lParam);

        case WM_RBUTTONDOWN: // RightClick in windows client area...
            return On_WM_RBUTTONDOWN (hWnd, wParam, lParam);

        case WM_PAINT:
            return On_WM_PAINT (hWnd, wParam, lParam);

        case WM_DESTROY:
            return On_WM_DESTROY (hWnd, wParam, lParam);

        default:
            return (DefWindowProc(hWnd, message, wParam, lParam));
   }
   return (0);
}


MAINWND.H

// data type and constant definitions used by mainwnd.c
#include <pdh.h>

typedef struct _CounterInfoBlock {
    struct _CounterInfoBlock *pNext;
    TCHAR   szCounterPath[MAX_PATH];
    HCOUNTER    hCounter;
    PPDH_RAW_COUNTER    pCounterArray;
    DWORD       dwFirstIndex;
    DWORD       dwNextIndex;
    DWORD       dwLastIndex;
    PDH_STATISTICS  pdhCurrentStats;
    double      dLastValue;
} CIB, *PCIB;

// global functions found in mainwnd.c
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


ABOUTDLG.C

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1996 - 1997 Microsoft Corporation.  All Rights Reserved.
//
// Module:  aboutdlg.c
//
// Overview:   contains the windows functions used by the about box
//
// PLATFORMS:  Windows NT (only)
//
//  FUNCTIONS:  About() About box DlgProc
//
//
// Windows Header Files:
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>

// C RunTime Header Files
#include <stdlib.h>

// Local Header Files
#include "statlist.h"
#include "winutils.h"
#include "aboutdlg.h"
//
//  FUNCTION: About(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for "About" dialog box
//       This version allows greater flexibility over the contents of the 'About' box,
//       by pulling out values from the 'Version' resource.
//
//  MESSAGES:
//
// WM_INITDIALOG - initialize dialog box
// WM_COMMAND    - Input received
//
//
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
   static  HFONT hfontDlg;    // Font for dialog text
   static   HFONT hFinePrint; // Font for 'fine print' in dialog
   DWORD   dwVerInfoSize;     // Size of version information block
   LPSTR   lpVersion;         // String pointer to 'version' text
   DWORD   dwVerHnd=0;        // An 'ignored' parameter, always '0'
   UINT    uVersionLen;
   WORD    wRootLen;
   BOOL    bRetCode;
   int     i;
   char    szFullPath[256];
   char    szResult[256];
   char    szGetName[256];
   DWORD dwVersion;
   char  szVersion[40];
   DWORD dwResult;

   switch (message) {
        case WM_INITDIALOG:
         // hide window while it's being constructed
         ShowWindow (hDlg, SW_HIDE);

         // crate and load the fonts to be used for the text
         hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                VARIABLE_PITCH | FF_SWISS, "");
         hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                VARIABLE_PITCH | FF_SWISS, "");

         // position the window in the center of the parent
         CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));

         // get the .exe path
         GetModuleFileName ((HINSTANCE)(GetWindowLong(hDlg, GWL_HINSTANCE)),
             szFullPath, sizeof(szFullPath));

         // Now lets dive in and pull out the version information:
         dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd);
         if (dwVerInfoSize) {
            LPSTR   lpstrVffInfo;
            HANDLE  hMem;
            hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
            lpstrVffInfo  = GlobalLock(hMem);
            GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpstrVffInfo);
            // The below 'hex' value looks a little confusing, but
            // essentially what it is, is the hexidecimal representation
            // of a couple different values that represent the language
            // and character set that we are wanting string values for.
            // 040904E4 is a very common one, because it means:
            //   US English, Windows MultiLingual characterset
            // Or to pull it all apart:
            // 04------        = SUBLANG_ENGLISH_USA
            // --09----        = LANG_ENGLISH
            // --11----        = LANG_JAPANESE
            // ----04E4 = 1252 = Codepage for Windows:Multilingual

            lstrcpy(szGetName, GetStringRes(IDS_VER_INFO_LANG));

            wRootLen = lstrlen(szGetName); // Save this position

            // Set the title of the dialog:
            lstrcat (szGetName, "ProductName");
            bRetCode = VerQueryValue((LPVOID)lpstrVffInfo,
               (LPSTR)szGetName,
               (LPVOID)&lpVersion,
               (UINT *)&uVersionLen);

            // Notice order of version and string...
            lstrcpy(szResult, "About ");
            lstrcat(szResult, lpVersion);

            // set dialog caption
            SetWindowText (hDlg, szResult);

            // Walk through the dialog items that we want to replace:
            for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) {
               GetDlgItemText(hDlg, i, szResult, sizeof(szResult));
               szGetName[wRootLen] = (char)0;
               lstrcat (szGetName, szResult);
               uVersionLen   = 0;
               lpVersion     = NULL;
               bRetCode      =  VerQueryValue((LPVOID)lpstrVffInfo,
                  (LPSTR)szGetName,
                  (LPVOID)&lpVersion,
                  (UINT *)&uVersionLen);

               if ( bRetCode && uVersionLen && lpVersion) {
               // Replace dialog item text with version info
                  lstrcpy(szResult, lpVersion);
                  SetDlgItemText(hDlg, i, szResult);
               }
               else
               {
                  dwResult = GetLastError();

                  wsprintf(szResult, GetStringRes(IDS_VERSION_ERROR), dwResult);
                  SetDlgItemText (hDlg, i, szResult);
               }
               SendMessage (GetDlgItem (hDlg, i), WM_SETFONT,
                  (UINT)((i==DLG_VERLAST)?hFinePrint:hfontDlg),
                  TRUE);
            } // for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++)

            GlobalUnlock(hMem);
            GlobalFree(hMem);

         } else {
            // No version information available.
         } // if (dwVerInfoSize)

            SendMessage (GetDlgItem (hDlg, IDC_LABEL), WM_SETFONT,
            (WPARAM)hfontDlg,(LPARAM)TRUE);

         // We are  using GetVersion rather then GetVersionEx
         // because earlier versions of Windows NT and Win32s
         // didn't include GetVersionEx:
         dwVersion = GetVersion();

         if (dwVersion < 0x80000000) {
            // Windows NT
            wsprintf (szVersion, "Microsoft Windows NT %u.%u (Build: %u)",
               (DWORD)(LOBYTE(LOWORD(dwVersion))),
               (DWORD)(HIBYTE(LOWORD(dwVersion))),
                    (DWORD)(HIWORD(dwVersion)) );
         } else if (LOBYTE(LOWORD(dwVersion))<4) {
            // Win32s
                wsprintf (szVersion, "Microsoft Win32s %u.%u (Build: %u)",
               (DWORD)(LOBYTE(LOWORD(dwVersion))),
               (DWORD)(HIBYTE(LOWORD(dwVersion))),
                    (DWORD)(HIWORD(dwVersion) & ~0x8000) );
         } else {
            // Windows 95
                wsprintf (szVersion, "Microsoft Windows 95 %u.%u",
                    (DWORD)(LOBYTE(LOWORD(dwVersion))),
                    (DWORD)(HIBYTE(LOWORD(dwVersion))) );
         }

         SetWindowText (GetDlgItem(hDlg, IDC_OSVERSION), szVersion);

         // now display the finished product
         ShowWindow (hDlg, SW_SHOW);
         return (TRUE);

      case WM_COMMAND:
         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
            EndDialog(hDlg, TRUE);
            DeleteObject (hfontDlg);
            DeleteObject (hFinePrint);
            return (TRUE);
         }
         break;
   }

    return FALSE;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by statlist.RC
//

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


STATLIST.C

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1996 - 1997 Microsoft Corporation.  All Rights Reserved.
//
// PROGRAM: STATLIST.c
//
// PURPOSE: Illustrates the use of the PDH counter browsing dialog
//          and the statistical functions of the PDH
//
// PLATFORMS:  Windows NT (only)
//
// FUNCTIONS:
//    WinMain() - calls initialization function, processes message loop
//    InitApplication() - Initializes window data nd registers window
//    InitInstance() -saves instance handle and creates main window
//    WindProc() Processes messages
//    About() - Process menssages for "About" dialog box
//    MyRegisterClass() - Registers the application's window class
//    CenterWindow() -  Centers one window over another
//
// SPECIAL INSTRUCTIONS: N/A
//

// Windows Header Files:
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>

// C RunTime Header Files
#include <stdlib.h>
#include <memory.h>

// Local Header Files
#include "statlist.h"

// Global Variables:

HINSTANCE hInst;      // current instance
char szAppName[100];  // Name of the app
char szTitle[100];    // The title bar text
char APPNAME[] = "Statlist"; // title of the app

// Foward declarations of functions included in this code module:

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
BOOL CenterWindow (HWND, HWND);
LPTSTR   GetStringRes (int id);

//
//  FUNCTION: MyRegisterClass(CONST WNDCLASS*)
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage is only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(CONST WNDCLASS *lpwc)
{
   HANDLE  hMod;
   FARPROC proc;
   WNDCLASSEX wcex;

   hMod = GetModuleHandle ("USER32");
   if (hMod != NULL) {

#if defined (UNICODE)
      proc = GetProcAddress (hMod, "RegisterClassExW");
#else
      proc = GetProcAddress (hMod, "RegisterClassExA");
#endif

      if (proc != NULL) {

         wcex.style         = lpwc->style;
         wcex.lpfnWndProc   = lpwc->lpfnWndProc;
         wcex.cbClsExtra    = lpwc->cbClsExtra;
         wcex.cbWndExtra    = lpwc->cbWndExtra;
         wcex.hInstance     = lpwc->hInstance;
         wcex.hIcon         = lpwc->hIcon;
         wcex.hCursor       = lpwc->hCursor;
         wcex.hbrBackground = lpwc->hbrBackground;
         wcex.lpszMenuName  = lpwc->lpszMenuName;
         wcex.lpszClassName = lpwc->lpszClassName;

         // Added elements for Windows 95:
         wcex.cbSize = sizeof(WNDCLASSEX);
         wcex.hIconSm = LoadIcon(wcex.hInstance, "SMALL");

         return (*proc)(&wcex);//return RegisterClassEx(&wcex);
      }
   }
   return (RegisterClass(lpwc));
}

//
//  FUNCTION: InitApplication(HANDLE)
//
//  PURPOSE: Initializes window data and registers window class
//
//  COMMENTS:
//
//       In this function, we initialize a window class by filling out a data
//       structure of type WNDCLASS and calling either RegisterClass or
//       the internal MyRegisterClass.
//
BOOL InitApplication(HINSTANCE hInstance)
{
    WNDCLASS  wc;
    HWND      hwnd;

    // Win32 will always set hPrevInstance to NULL, so lets check
    // things a little closer. This is because we only want a single
    // version of this app to run at a time
    hwnd = FindWindow (szAppName, NULL);
    if (hwnd) {
        // We found another version of ourself. Lets defer to it:
        if (IsIconic(hwnd)) {
            ShowWindow(hwnd, SW_RESTORE);
        }
        SetForegroundWindow (hwnd);

        // If this app actually had any functionality, we would
        // also want to communicate any action that our 'twin'
        // should now perform based on how the user tried to
        // execute us.
        return FALSE;
        }

        // Fill in window class structure with parameters that describe
        // the main window.
        wc.style         = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc   = (WNDPROC)WndProc;
        wc.cbClsExtra    = 0;
        wc.cbWndExtra    = 0;
        wc.hInstance     = hInstance;
        wc.hIcon         = LoadIcon (hInstance, szAppName);
        wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

        wc.lpszMenuName  = szAppName;
        wc.lpszClassName = szAppName;

        // Register the window class and return success/failure code.
        return RegisterClass(&wc);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
   DWORD dwStatus;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szAppName, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
      NULL, NULL, hInstance, NULL);

   if (!hWnd) {
      dwStatus = GetLastError();
      return (FALSE);
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return (TRUE);
}

//
//  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
//
//  PURPOSE: Entry point for the application.
//
//  COMMENTS:
//
// This function initializes the application and processes the
// message loop.
//
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
   MSG msg;
   HANDLE hAccelTable;

   // Initialize global strings
   lstrcpy (szAppName, APPNAME);
   LoadString (hInstance, IDS_APP_TITLE, szTitle, 100);

   if (!hPrevInstance) {
      // Perform instance initialization:
      if (!InitApplication(hInstance)) {
         return (FALSE);
      }
   }

   // Perform application initialization:
   if (!InitInstance(hInstance, nCmdShow)) {
      return (FALSE);
   }

   hAccelTable = LoadAccelerators (hInstance, szAppName);

   // Main message loop:
   while (GetMessage(&msg, NULL, 0, 0)) {
      if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg)) {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
   }

   return (msg.wParam);

   lpCmdLine; // This will prevent 'unused formal parameter' warnings
}


STATLIST.H

//---------------------------------------------------------------------------
//  STATLIST.H
//---------------------------------------------------------------------------

// Makes it easier to determine appropriate code paths:
#define IS_WIN32 TRUE
#define IS_NT      IS_WIN32 && (BOOL)(GetVersion() < 0x80000000)
#define IS_WIN32S  IS_WIN32 && (BOOL)(!(IS_NT) && (LOBYTE(LOWORD(GetVersion()))<4))
#define IS_WIN95 (BOOL)(!(IS_NT) && !(IS_WIN32S)) && IS_WIN32

#define IDM_NEW            101
#define IDM_OPEN           102
#define IDM_SAVE           103
#define IDM_SAVEAS         104
#define IDM_EXIT           105

#define IDM_ADD_COUNTERS   201
#define IDM_DELETE_COUNTER 202
#define IDM_CLEAR_ALL      203
#define IDM_SETTINGS       204

#define IDM_GET_DATA       300

#define IDM_HELPTOPICS     401
#define IDM_ABOUT          402

#define IDC_STATIC -1

#define DLG_VERFIRST        1000
#define IDC_COMPANY         DLG_VERFIRST
#define IDC_FILEDESC        DLG_VERFIRST+1
#define IDC_PRODVER         DLG_VERFIRST+2
#define IDC_COPYRIGHT       DLG_VERFIRST+3
#define IDC_OSVERSION       DLG_VERFIRST+4
#define IDC_TRADEMARK       DLG_VERFIRST+5
#define DLG_VERLAST         DLG_VERFIRST+5

#define IDC_LABEL           DLG_VERLAST+1

#define IDS_APP_TITLE       500
#define IDS_DISPLAYCHANGED  501
#define IDS_VER_INFO_LANG   502
#define IDS_VERSION_ERROR   503
#define IDS_NO_HELP         504

extern char APPNAME[];


WINUTILS.C

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1996 - 1997 Microsoft Corporation.  All Rights Reserved.
//
// PROGRAM: WINUTILS.c
//
// PURPOSE: general purpose MS-Windows utility functions
//
// PLATFORMS:  Windows NT (only)
//
// FUNCTIONS:
//    CenterWindow() -  Centers one window over another
//    GetStringRes() -  retrieves a string resource
//
// SPECIAL INSTRUCTIONS: N/A
//

//  system include files
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>

// local include files
#include "winutils.h"

//
//   FUNCTION: CenterWindow(HWND, HWND)
//
//   PURPOSE: Centers one window over another.
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
//       This functionwill center one window over another ensuring that
//    the placement of the window is within the 'working area', meaning
//    that it is both within the display limits of the screen, and not
//    obscured by the tray or other framing elements of the desktop.
BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
{
   RECT    rChild, rParent, rWorkArea;
   int     wChild, hChild, wParent, hParent;
   int     xNew, yNew;
   BOOL  bResult;

   // Get the Height and Width of the child window
   GetWindowRect (hwndChild, &rChild);
   wChild = rChild.right - rChild.left;
   hChild = rChild.bottom - rChild.top;

   // Get the Height and Width of the parent window
   GetWindowRect (hwndParent, &rParent);
   wParent = rParent.right - rParent.left;
   hParent = rParent.bottom - rParent.top;

   // Get the limits of the 'workarea'
   bResult = SystemParametersInfo(
      SPI_GETWORKAREA,  // system parameter to query or set
      sizeof(RECT),
      &rWorkArea,
      0);
   if (!bResult) {
      rWorkArea.left = rWorkArea.top = 0;
      rWorkArea.right = GetSystemMetrics(SM_CXSCREEN);
      rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
   }

   // Calculate new X position, then adjust for workarea
   xNew = rParent.left + ((wParent - wChild) /2);
   if (xNew < rWorkArea.left) {
      xNew = rWorkArea.left;
   } else if ((xNew+wChild) > rWorkArea.right) {
      xNew = rWorkArea.right - wChild;
   }

   // Calculate new Y position, then adjust for workarea
   yNew = rParent.top  + ((hParent - hChild) /2);
   if (yNew < rWorkArea.top) {
      yNew = rWorkArea.top;
   } else if ((yNew+hChild) > rWorkArea.bottom) {
      yNew = rWorkArea.bottom - hChild;
   }

   // Set it, and return
   return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}

//---------------------------------------------------------------------------
//
// FUNCTION:    GetStringRes (int id INPUT ONLY)
//
// COMMENTS:    Load the resource string with the ID given, and return a
//              pointer to it.  Notice that the buffer is common memory so
//              the string must be used before this call is made a second time.
//
//---------------------------------------------------------------------------

LPTSTR   GetStringRes (int id)
{
  static TCHAR buffer[MAX_PATH];

  buffer[0]=0;
  LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
  return buffer;
}


WINUTILS.H

// global functions in winutils.c

BOOL CenterWindow (HWND hwndChild, HWND hwndParent);
LPTSTR   GetStringRes (int id);


TYPEPERF.C

/*++

  typeperf.c

  program to demonsrate the use of the PDH performance data collection DLL's

    this program is a Window NT console app that accepts as arguments
    PDH counter paths and then samples the valid counter paths at a 1 second
    interval and writes the output to the console output with a timestamp in
    the format of a Comma Separated Variable file.

--*/
#if UNICODE
#ifndef _UNICODE
#define _UNICODE            1
#endif
#define tmain   wmain
#else
#define tmain   main
#endif

#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <assert.h>
#include <tchar.h>
#include <pdh.h>

#define SAMPLE_INTERVAL_MS  1000

void
DisplayCommandLineHelp ()
{
    _tprintf (TEXT("\n\nOne or more valid counter paths must be specified as a command line"));
    _tprintf (TEXT("\nargument."));
    _tprintf (TEXT("\nThe default sample interval is one second and the default output is"));
    _tprintf (TEXT("\na Comma Separated Variable (CSV) string"));
    return;
}

int
tmain (int argc,
      LPTSTR argv[])
{
    HQUERY          hQuery;
    HCOUNTER        *pCounterArray;
    HCOUNTER        *pThisCounterHandle;
    PDH_STATUS      pdhStatus;
    PDH_FMT_COUNTERVALUE   fmtValue;
    DWORD           ctrType;
    SYSTEMTIME      stSampleTime;

    int             nThisArg;
    int             nValidCounters;

    // the command line arguments are the counters to sample. the
    // sample interval is 1 second.

    // count the arguments in the command line
    if (argc < 2) {
        // not enough arguments in the command line so
        // display explanation and exit
        DisplayCommandLineHelp ();
        return 1;
    }
    // there's at least one argument (counter) in the command line
    // so process it/them

    // open the PDH query object

    pdhStatus = PdhOpenQuery (0, 0, &hQuery);
    assert (pdhStatus == ERROR_SUCCESS);
    // allocate the counter handle array. allocate room for
    // one handle per command line arg, not including the
    // executable file name
    pCounterArray = (HCOUNTER *)GlobalAlloc(GPTR,
        (sizeof(HCOUNTER) * (argc -1)));
    assert (pCounterArray != NULL);

    nValidCounters = 0;
    _tprintf (TEXT("\n\"Sample Time\""));
    for (nThisArg = 1; nThisArg < argc; nThisArg++) {
        pdhStatus = PdhAddCounter (hQuery,
            argv[nThisArg], 0, &pCounterArray[nThisArg-1]);
        if (pdhStatus != ERROR_SUCCESS) {
            // the counter didn't get added to the query, probably because
            // the path wasn't specified correctly or is not present
            printf ("\n\"%s\" is not a valid counter path", argv[nThisArg]);
            pCounterArray[nThisArg-1] = NULL;
        } else {
            // print counter name in heading line
            _tprintf (TEXT(",\"%s\""), argv[nThisArg]);
            nValidCounters++;
        }
    }

    // if there is at least one valid counter, then loop
    //  until a key is pressed
    if (nValidCounters > 0) {
        // "prime" counters that need 2 values to display a formatted value
        pdhStatus = PdhCollectQueryData (hQuery);
        assert (pdhStatus == ERROR_SUCCESS);
        // loop until completion event occurs, in this case it's a
        // key press.
        while (!_kbhit()) {
            // wait one interval....
            Sleep(SAMPLE_INTERVAL_MS);
            // get sample time
            GetLocalTime (&stSampleTime);
            // get the current data values
            pdhStatus = PdhCollectQueryData (hQuery);
            assert (pdhStatus == ERROR_SUCCESS);
            // print time stamp
            _tprintf (TEXT("\n\"%2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d.%3.3d\""),
                stSampleTime.wMonth, stSampleTime.wDay, stSampleTime.wYear,
                stSampleTime.wHour, stSampleTime.wMinute, stSampleTime.wSecond,
                stSampleTime.wMilliseconds);
            // find first valid counter in the list
            nThisArg = 0;
            pThisCounterHandle = &pCounterArray[nThisArg];
            while ((*pThisCounterHandle == NULL) && (nThisArg < argc)) {
                nThisArg++;
                pThisCounterHandle++;
            }
            // pThisCounterHandle should point to the first valid counter
            // in the counter handle array.
            // so go from here to the end of the array and print the current
            // daa values

            while (nThisArg < (argc-1)) {
                if (*pThisCounterHandle != NULL) {
                    // get the current value for this counter
                    pdhStatus = PdhGetFormattedCounterValue (
                        *pThisCounterHandle,
                        PDH_FMT_DOUBLE,
                        &ctrType,
                        &fmtValue);
                    if (pdhStatus == ERROR_SUCCESS) {
                        _tprintf (TEXT(",\"%.20g\""), fmtValue.doubleValue);
                    } else {
                        _tprintf (TEXT(".\"-1\"")); // error value
                    }
                }
                pThisCounterHandle++;
                nThisArg++;
            }
        }
    } else {
        printf ("\nNo counters to monitor.");
    }

    // clean up PDH interface and leave
    pdhStatus = PdhCloseQuery (hQuery);
    assert (pdhStatus == ERROR_SUCCESS);
    return 0;
}


README.TXT

APPMEM

Application Heap usage counters

These DLL's allow an application developer to instrument the heap
usage of an application program as determined by the calls to
GlobalAlloc, GlobalReAlloc and GlobalFree. The frequency of calls and
amount of memory used by these calls can be tracked via the
performance registry and Perfmon.

Installation

Once the project has been built, the performance counters can be
installed by following the steps below. The files will be found in
the pub subdirectory with the .dlls in the pub\<proc> dir.

    copy the appropriate dll's to the %systemroot%\system32 directory
    run: regini appmem.reg
    run: lodctr appmem.ini

Use:

To have your application track memory usage, it needs to call the
following functions instead of the standard ones. These can be
conditionally compiled (or not) if you don't wish to use them all the
time.

        Std. Function       Tracking Function
        ----------------    ---------------------
        GlobalAlloc         GlobalAllocP
        GlobalReAlloc       GlobalReAllocP
        GlobalFree          GlobalFreeP

The .EXE will need to link with the APPMEMA.LIB in order to find
these new functions.

Once successfully compiled and linked, start PERFMON, then start the
executable program. In perfmon, select the "ADD to..." dialog and add
the program's instance of the desired counters to the display and
begin tracking the heap usage of the application.


APPMEMA.C

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    appmema.c

Abstract:

    application memory performance counter interface functions

    This file contains the memory allocation function "wrappers"
    to allow monitoring of the memory usage by a performance monitoring
    application (e.g. PERFMON).

Revision History

    30 Aug 1995     Bob Watson (a-robw)     Created

--*/
#include <windows.h>
#include "appmema.h"
#include "..\pub\appmem.h"

HGLOBAL
GlobalAllocP (
    UINT    fuFlags,
    DWORD   cbBytes
)
{
    HGLOBAL hReturn;
    DWORD   dwSize;

    hReturn = GlobalAlloc (fuFlags, cbBytes);

    if (hReturn != NULL) {
        // only collect statistics on successful calls
        if (pAppData != NULL) {
            // only update data if data block is valid
            // update call count
            pAppData->dwAllocCalls++;
            dwSize = GlobalSize(hReturn);
            pAppData->dwApplicationBytes += dwSize;
        }
    }
    return hReturn;
}

HGLOBAL
GlobalFreeP (
    HGLOBAL hglbMem
)
{
    HGLOBAL hReturn;
    DWORD   dwSize;

    // get size of block before freeing it
    dwSize = GlobalSize (hglbMem);
    if (dwSize > 0) {
        // size was obtained OK so free the block
        hReturn = GlobalFree (hglbMem);
        if (hReturn == NULL) {
            // memory was freed
            if (pAppData != NULL) {
                // update call count
                pAppData->dwFreeCalls++;
                pAppData->dwApplicationBytes -= dwSize;
            }
        } else {
            // memory was not freed so don't change data
        }
    } else {
        // unable to read memory size
    }
    return hReturn;
}

HGLOBAL
GlobalReAllocP (
    HGLOBAL hglbMem,
    DWORD   cbBytes,
    UINT    fuFlags
)
{
    HGLOBAL hReturn;
    DWORD   dwBeforeSize = 0;
    DWORD   dwAfterSize = 0;

    // get size of block before re-alloc-ing it
    dwBeforeSize = GlobalSize (hglbMem);
    if (dwBeforeSize > 0) {
        // size was obtained OK so realloc the block
        hReturn = GlobalReAlloc (hglbMem, cbBytes, fuFlags);
        if (hReturn != NULL) {
            // memory was re alloc'd so get the new size
            dwAfterSize = GlobalSize (hReturn);
        } else {
            // memory was not re-alloced so set after size to 0
        }
        if (pAppData != NULL) {
            // update call count
            pAppData->dwReAllocCalls++;
            pAppData->dwApplicationBytes -= dwBeforeSize;
            pAppData->dwApplicationBytes += dwAfterSize;
        }
    } else {
        // unable to read memory size nothing has changed
        // return original pointer
        hReturn = hglbMem;
    }
    return hReturn;
}


APPMEMA.H

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    appmema.h

Abstract:

    application memory performance counter definitions

Revision History

    30 Aug 1995     Bob Watson (a-robw)     Created

--*/
#ifndef _APPMEMA_H_
#define _APPMEMA_H_

#include "..\inc\appmemi.h"

struct  _APPMEM_DATA_HEADER *pDataHeader;
struct  _APPMEM_INSTANCE    *pAppData;

extern  HANDLE  hAppMemMutex;

#endif // _APPMEMA_H_


DLLINIT.C

/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    dllinit.c

Abstract:

    This module contians the DLL attach/detach event entry point for
    the Application memory usage performance DLL.

    When the dll is opened by the calling application. The initialization
    function (DllProcessAttach) opens or creates the shared memory file
    that is used to communicate data from the application to the performance
    monitor extensible counter DLL. This shared memory file is used by all
    applications that have been compiled to provide the memory statistics.

    The shared memory file is formatted by the first application to
    create it. The format of the memory is a data header (at the base
    of the address space) followed by an array of APPMEM_INSTANCE structures.
    The APPMEM_INSTANCE structures are managed as two linked lists: a
    Free List and an InUse List. The pointers to the first entry in each
    list are maintained in the data header structure. As applications are
    started, the first item in the Free list is moved to the InUse List
    and as applications finish with the data block, their entries are moved
    back to the Head of the Free List. All operations on the data in
    this shared memory file are protected by a mutex such that only
    one process can read or write to the file at a time.

    Note that references are by offset from the base of the memory
    block not absolute virtual addresses. This is because the same
    shared memory file may be mapped to different addresses in
    different processes making a virtual address valid within the
    context of a single process only.

Author:

    Bob Watson (a-robw) Aug 95

Revision History:

--*/

#include <windows.h>
#include "appmema.h"

HANDLE ThisDLLHandle = NULL;

HANDLE  hAppMemSharedMemory = NULL;// Handle of counter Shared Memory
HANDLE  hAppMemMutex = NULL;            // sync mutex for memory

PAPPMEM_DATA_HEADER pDataHeader = NULL; // pointer to header of shared mem
PAPPMEM_INSTANCE    pAppData = NULL;    // pointer to the app data for this app

static
BOOL
TrimProcessName (
    LPCWSTR  wcszLongName,
    LPWSTR  wcszShortName
)
/*++

Description:

    strips the leading directory path and trailing file extension from the
    input path string to return the base filename.

Arguments:

    wcszLongName    pointer to a Wide Character string containing the
                    process's image file path name

    wcszShortName   buffer to receive the "short" version of the image
                    file name, i.e. the base file name with no extension or
                    directory path.

NOTE: the buffer size is not checked!!! The caller must guarantee sufficient
    space for the conversion.

Return Value:

    TRUE if name trimmed successfully
    FALSE if error

--*/
{
    DWORD   dwLength;
    LPWSTR  pSlash;
    LPWSTR  pPeriod;
    LPWSTR  pThisChar;
    LPWSTR  pSrc, pDest;
    WORD    wThisChar;

    // validate the input arguments
    if ((wcszLongName == NULL) || (wcszShortName == 0)) {
        return FALSE;
    }

    // get the length of the string passed in
    dwLength = lstrlenW (wcszLongName);

    if (*wcszLongName > 0) {   // some name has been passed in

        pSlash =
         pPeriod =
         pThisChar = (LPWSTR)wcszLongName;
        wThisChar = 0;

        //
        //  go from beginning to end and find last backslash and
        //  last period in name
        //

        while (*pThisChar != 0) { // go until null
            if (*pThisChar == L'\\') {
                pSlash = pThisChar;
            } else if (*pThisChar == L'.') {
                pPeriod = pThisChar;
            }
            pThisChar++;    // point to next char
            wThisChar++;
            if ((DWORD)wThisChar >= dwLength) {
                break;
            }
        }

        // if pPeriod is still pointing to the beginning of the
        // string, then no period was found

        if (pPeriod == (LPWSTR)wcszLongName) {
            pPeriod = pThisChar; // set to end of string;
        } else {
            // if a period was found, then see if the extension is
            // .EXE, if so leave it, if not, then use end of string
            // (i.e. include extension in name)

            if (lstrcmpiW(pPeriod, L".EXE") != 0) {
                pPeriod = pThisChar;
            }
        }

        if (pSlash != wcszLongName) {
            pSlash++; // point to first char past slash
        }

        // copy characters between period (or end of string) and
        // slash (or start of string) to make image name

        pSrc = pSlash;
        pDest = wcszShortName;

        while (pSrc < pPeriod) {
            *pDest++ = *pSrc++;
        }
        *pDest = 0;
    }
    return TRUE;
}

static
BOOL
DllProcessAttach (
    IN  HANDLE DllHandle
)
/*++

Description:

    Initializes the interface to the performance counters DLL by
    opening the Shared Memory file used to communicate statistics
    from the application to the counter DLL. If the Shared memory
    file does not exist, it is created, formatted and initialized.
    If the file has already been created and formatted, then the
    next available APPMEM_INSTANCE entry is moved from the free list
    to the InUse list and the corresponding pointer is saved for
    subsequent use by this application

--*/
{
    LONG    status;
    TCHAR   szMappedObject[] = SHARED_MEMORY_OBJECT_NAME;
    WCHAR   szLongName[MAX_PATH];

    // save this DLL handle
    ThisDLLHandle = DllHandle;

    // disable thread attach & detach calls to save the overhead
    // since we don't care about them.
    DisableThreadLibraryCalls (DllHandle);

    // open & initialize shared memory file
    SetLastError (ERROR_SUCCESS);   // just to clear it out

    // open/create shared memory used by the application to pass performance values
    status = GetSharedMemoryDataHeader (
        &hAppMemSharedMemory, &hAppMemMutex, &pDataHeader,
        FALSE); // read/write access is required
    // here the memory block should be initialized and ready for use
    if (status == ERROR_SUCCESS) {
        if (pDataHeader->dwFirstFreeOffset != 0) {
            // then there are blocks left so get the next free
            pAppData = FIRST_FREE(pDataHeader);
            // update free list to make next item the first in list
            pDataHeader->dwFirstFreeOffset = pAppData->dwOffsetOfNext;

            // insert the new item into the head of the in use list
            pAppData->dwOffsetOfNext = pDataHeader->dwFirstInUseOffset;
            pDataHeader->dwFirstInUseOffset = (DWORD)((LPBYTE)pAppData -
                                                        (LPBYTE)pDataHeader);

            // now initialize this instance's data
            pAppData->dwProcessId = GetCurrentProcessId();
            pAppData->hProcessHeap = NULL;

            pAppData->dwApplicationBytes = 0;
            pAppData->dwAllocCalls = 0;
            pAppData->dwReAllocCalls = 0;
            pAppData->dwFreeCalls = 0;
            pAppData->dwReserved1 = 0;
            pAppData->dwReserved2 = 0;

            GetModuleFileNameW (NULL,
                szLongName, MAX_PATH);

            TrimProcessName (
                szLongName,
                pAppData->wcszInstanceName);

            pDataHeader->dwInstanceCount++;    // increment count
        } else {
            // no more free slots left
        }
    } else {
        // unable to open shared memory file
        // even though this is an error we should return true so as to
        // not abort the application. No performance data will be
        // collected though.
    }
    return TRUE;
}

static
BOOL
DllProcessDetach (
    IN  HANDLE DllHandle
)
{
    BOOL    bFreeMutex;
    PAPPMEM_INSTANCE    pPrevItem;

    // remove instance for this app
    if ((pAppData != NULL) && (pDataHeader != NULL)) {
        // lock memory block
        if (hAppMemMutex != NULL) {
            if (WaitForSingleObject (hAppMemMutex, SHARED_MEMORY_MUTEX_TIMEOUT) == WAIT_FAILED) {
                // unable to obtain a lock
                bFreeMutex = FALSE;
            } else {
                bFreeMutex = TRUE;
            }
        } else {
            bFreeMutex = FALSE;
        }
        // zero the fields out first
        pAppData->dwProcessId = 0;
        pAppData->hProcessHeap = NULL;
        pAppData->dwApplicationBytes = 0;
        pAppData->dwAllocCalls = 0;
        pAppData->dwReAllocCalls = 0;
        pAppData->dwFreeCalls = 0;
        memset (&pAppData->wcszInstanceName[0], 0, (MAX_SIZEOF_INSTANCE_NAME * sizeof (WCHAR)));
        pAppData->dwReserved1 = 0;
        pAppData->dwReserved2 = 0;
        // move from in use (busy) list back to the free list
        if ((pDataHeader->dwFirstFreeOffset != 0) && (pDataHeader->dwFirstInUseOffset != 0)) {
            // find previous item in busy list
            if (FIRST_INUSE(pDataHeader) != pAppData) {
                // not the first so walk down the list
                pPrevItem = FIRST_INUSE(pDataHeader);
                while (APPMEM_INST(pDataHeader, pPrevItem->dwOffsetOfNext) != pAppData) {
                    pPrevItem = APPMEM_INST(pDataHeader, pPrevItem->dwOffsetOfNext);
                    if (pPrevItem->dwOffsetOfNext == 0) break; // end of list
                }
                if (APPMEM_INST(pDataHeader, pPrevItem->dwOffsetOfNext) == pAppData) {
                    APPMEM_INST(pDataHeader, pPrevItem->dwOffsetOfNext)->dwOffsetOfNext =
                        pAppData->dwOffsetOfNext;
                } else {
                    // it was never in the busy list (?!?)
                }
            } else {
                // this is the first in the list so update it
                pDataHeader->dwFirstInUseOffset = pAppData->dwOffsetOfNext;
            }
            // here, pAppData has been removed from the InUse list and now
            // it must be inserted back at the beginning of the free list
            pAppData->dwOffsetOfNext = pDataHeader->dwFirstFreeOffset;
            pDataHeader->dwFirstFreeOffset = (DWORD)((LPBYTE)pAppData - (LPBYTE)pDataHeader);
        }
    }

    // decrement instance counter
    pDataHeader->dwInstanceCount--;    // decrement count

    if (bFreeMutex) {
        ReleaseMutex (hAppMemMutex);
    }

    // close shared memory file handle

    if (hAppMemSharedMemory != NULL) CloseHandle (hAppMemSharedMemory);
    if (hAppMemMutex != NULL)       CloseHandle(hAppMemMutex);

    // clear pointers
    hAppMemSharedMemory = NULL;
    pDataHeader = NULL;
    pAppData = NULL;

    return TRUE;
}

BOOL
__stdcall
DllInit(
    IN HANDLE DLLHandle,
    IN DWORD  Reason,
    IN LPVOID ReservedAndUnused
)
{
    ReservedAndUnused;

    switch(Reason) {
        case DLL_PROCESS_ATTACH:
            return DllProcessAttach (DLLHandle);

        case DLL_PROCESS_DETACH:
            return DllProcessDetach (DLLHandle);

        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            return TRUE;
    }
}


APPMEMI.H

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright 1995 - 1998 Microsoft Corporation

Header Name:

    appmem.h

Abstract:

    Contains the global definitions for use by the performance DLL and
    the DLL used by the application to allocate and track memory. The
    definitions in this file ARE NOT used by the application programmer

Created:

    Bob Watson 24 august 1995

Revision History

    None

--*/
#ifndef _APPMEM_H_
#define _APPMEM_H_

#define MAX_SIZEOF_INSTANCE_NAME    32

typedef struct _APPMEM_INSTANCE {
    DWORD   dwOffsetOfNext;     // offset from mem base to next item in list
    DWORD   dwProcessId;        // id of process using this instance
    HANDLE  hProcessHeap;       // handle of process's default heap
    DWORD   dwApplicationBytes; // current count of heap bytes allocated
                                //  by the app mem allocation DLL functions
    DWORD   dwAllocCalls;       // number of memory allocation calls
    DWORD   dwReAllocCalls;     // number of Re-Allocation calls
    DWORD   dwFreeCalls;        // number of memory free calls
    WCHAR   wcszInstanceName[MAX_SIZEOF_INSTANCE_NAME]; // SZ instance name
    DWORD   dwReserved1;        // unused
    DWORD   dwReserved2;        // unused
} APPMEM_INSTANCE, *PAPPMEM_INSTANCE;

#define SHARED_MEMORY_ITEM_COUNT    100
#define SHARED_MEMORY_OBJECT_SIZE   (sizeof(PDWORD) + (sizeof(PAPPMEM_INSTANCE) * 2) + (SHARED_MEMORY_ITEM_COUNT * sizeof(APPMEM_INSTANCE)))
#define SHARED_MEMORY_OBJECT_NAME   (TEXT("APPMEM_PERF_DATA"))

#define SHARED_MEMORY_MUTEX_NAME    (TEXT("APPMEM_PERF_DATA_MUTEX"))
#define SHARED_MEMORY_MUTEX_TIMEOUT ((DWORD)1000L)

typedef struct _APPMEM_DATA_HEADER {
    DWORD               dwInstanceCount;        // number of entries In Use
    DWORD               dwFirstInUseOffset;     // offset from mem base
    DWORD               dwFirstFreeOffset;      // offset from mem base
} APPMEM_DATA_HEADER, *PAPPMEM_DATA_HEADER;

#define     FIRST_FREE(base)    ((PAPPMEM_INSTANCE)((LPBYTE)(base) + ((PAPPMEM_DATA_HEADER)(base))->dwFirstFreeOffset))
#define     FIRST_INUSE(base)   ((PAPPMEM_INSTANCE)((LPBYTE)(base) + ((PAPPMEM_DATA_HEADER)(base))->dwFirstInUseOffset))
#define     APPMEM_INST(base, offset)   ((PAPPMEM_INSTANCE)((LPBYTE)(base) + (DWORD)(offset)))

LONG
_stdcall
GetSharedMemoryDataHeader (
    IN  HANDLE              *phAppMemSharedMemory,
    IN  HANDLE              *phAppMemMutex,
    IN  PAPPMEM_DATA_HEADER *pHeader,
    IN  BOOL                bReadOnlyAccess
);

#endif //_APPMEM_H_


DATAMEM.C

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    datamem.c

Abstract:

    a file containing the constant data structures used by the Performance
    Monitor data for the application heap performance counter object

    This file contains a set of constant data structures which are
    currently defined for the application heap Objects.

Created:

    Bob Watson  24-aug-95

Revision History:

    None.

--*/
//
//  Include Files
//

#include <windows.h>
#include <winperf.h>
#include "..\pub\memctrnm.h"    // offset definitions
#include "datamem.h"            // data structure definitions

// dummy local variable
static    APPMEM_COUNTERS     ac;

//
//  Constant structure initializations
//      defined in datamem.h
//

APPMEM_DATA_DEFINITION AppMemDataDefinition = {
    {sizeof(APPMEM_DATA_DEFINITION) + sizeof(APPMEM_COUNTERS),
    sizeof(APPMEM_DATA_DEFINITION),
    sizeof(PERF_OBJECT_TYPE),
    APPMEMOBJ,
    0,
    APPMEMOBJ,
    0,
    PERF_DETAIL_NOVICE,
    (sizeof(APPMEM_DATA_DEFINITION)-sizeof(PERF_OBJECT_TYPE))/
        sizeof(PERF_COUNTER_DEFINITION),
    0,  // application memory bytes is the default counter
    0,  // 0 instances to start with
    0,  // unicode instance names
    {0,0},
    {0,0}
    },
    {sizeof(PERF_COUNTER_DEFINITION),
    APPMEMALLOC,
    0,
    APPMEMALLOC,
    0,
    -5,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_RAWCOUNT,
    sizeof(ac.dwAppMemBytesAllocated),
    0,
    },
    {sizeof(PERF_COUNTER_DEFINITION),
    APPALLOCS,
    0,
    APPALLOCS,
    0,
    -1,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_RAWCOUNT,
    sizeof(ac.dwAppMemAllocs),
    0
    },
    {sizeof(PERF_COUNTER_DEFINITION),
    APPALLOCSSEC,
    0,
    APPALLOCSSEC,
    0,
    0,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_COUNTER,
    sizeof(ac.dwAppMemAllocsSec),
    0
    },
    {sizeof(PERF_COUNTER_DEFINITION),
    APPREALLOCS,
    0,
    APPREALLOCS,
    0,
    -1,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_RAWCOUNT,
    sizeof(ac.dwAppMemReAllocs),
    0
    },
    {sizeof(PERF_COUNTER_DEFINITION),
    APPREALLOCSSEC,
    0,
    APPREALLOCSSEC,
    0,
    0,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_COUNTER,
    sizeof(ac.dwAppMemReAllocsSec),
    0
    },
    {sizeof(PERF_COUNTER_DEFINITION),
    APPFREES,
    0,
    APPFREES,
    0,
    -1,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_RAWCOUNT,
    sizeof(ac.dwAppMemFrees),
    0
    },
    {sizeof(PERF_COUNTER_DEFINITION),
    APPFREESSEC,
    0,
    APPFREESSEC,
    0,
    0,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_COUNTER,
    sizeof(ac.dwAppMemFreesSec),
    0
    }
};


DATAMEM.H

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

      datamem.h

Abstract:

    Header file for the application help performance Object data
    definitions

    This file contains definitions to construct the dynamic data
    which is returned by the Configuration Registry.  Data from
    various system API calls is placed into the structures shown
    here.

Author:

   Bob Watson 24-aug-95

Revision History:

--*/

#ifndef _DATAMEM_H_
#define _DATAMEM_H_

//
//  pack on 8-byte boundries so any longlong fields won't cause alignment
//  faults on RISC platforms
//
//
#pragma pack (8)

//
//  Extensible Object definitions
//

//  Update the following sort of define when adding an object type.

#define APP_MEM_NUM_PERF_OBJECT_TYPES 1

//----------------------------------------------------------------------------

//  Application memory counters
//
//
//  This is the counter structure presently returned by Nbf for
//  each Resource.  Each Resource is an Instance, named by its number.
//
typedef struct _APPMEM_DATA_DEFINITION {
    PERF_OBJECT_TYPEAppMemObjectType;
    PERF_COUNTER_DEFINITION AppMemBytesAllocated;
    PERF_COUNTER_DEFINITION AppMemAllocs;
    PERF_COUNTER_DEFINITION AppMemAllocsSec;
    PERF_COUNTER_DEFINITION AppMemReAllocs;
    PERF_COUNTER_DEFINITION AppMemReAllocsSec;
    PERF_COUNTER_DEFINITION AppMemFrees;
    PERF_COUNTER_DEFINITION AppMemFreesSec;
} APPMEM_DATA_DEFINITION;

typedef struct _APPMEM_COUNTERS {
    PERF_COUNTER_BLOCK  CounterBlock;
    DWORD               dwAppMemBytesAllocated;
    DWORD               dwAppMemAllocs;
    DWORD               dwAppMemAllocsSec;
    DWORD               dwAppMemReAllocs;
    DWORD               dwAppMemReAllocsSec;
    DWORD               dwAppMemFrees;
    DWORD               dwAppMemFreesSec;
} APPMEM_COUNTERS, *PAPPMEM_COUNTERS;

#pragma pack ()

#endif //_DATAMEM_H_


PERFMEM.C

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    perfmem.c

Abstract:

    This file implements the Extensible Objects for  the application
    memory object

Created:

    Bob Watson 24 august 1995

Revision History

    None

--*/
//
//  Include Files
//
#include <windows.h>
#include <string.h>
#include <winperf.h>
#include "..\inc\appmemi.h"     // definitions shared with the app. dll
#include "memctrs.h"            // error message definition
#include "perfmsg.h"            // message utilities
#include "perfutil.h"           // perf data utilities
#include "datamem.h"            // perf data structure definitions

//
//  References to constants which initialize the Object type definitions
//
extern APPMEM_DATA_DEFINITION AppMemDataDefinition;

static DWORD   dwOpenCount = 0;        // count of "Open" threads
static BOOL    bInitOK = FALSE;        // true = DLL initialized OK

//
// App Mem counter data structures
//

static  HANDLE  hAppMemSharedMemory = NULL; // Handle of counter Shared Memory
static  HANDLE  hAppMemMutex = NULL;        // sync mutex for memory

static  PAPPMEM_DATA_HEADER pDataHeader = NULL; // pointer to header of shared mem
//
//  Function Prototypes
//
//      these are used to insure that the data collection functions
//      accessed by Perflib will have the correct calling format.
//

PM_OPEN_PROC    OpenAppMemPerformanceData;
PM_COLLECT_PROC CollectAppMemPerformanceData;
PM_CLOSE_PROC   CloseAppMemPerformanceData;

DWORD APIENTRY
OpenAppMemPerformanceData(
    LPWSTR lpDeviceNames
)
/*++

Routine Description:

    This routine will open and map the memory used by the App Mem DLL to
    pass performance data in. This routine also initializes the data
    structures used to pass data back to the registry

Arguments:

    Pointer to object ID of each device to be opened

Return Value:

    None.

--*/

{
    LONG status;
    HKEY hKeyDriverPerf;
    DWORD size;
    DWORD type;
    DWORD dwFirstCounter;
    DWORD dwFirstHelp;
    APPMEM_COUNTERS     ac;

    //
    //  Since WINLOGON is multi-threaded and will call this routine in
    //  order to service remote performance queries, this library
    //  must keep track of how many times it has been opened (i.e.
    //  how many threads have accessed it). the registry routines will
    //  limit access to the initialization routine to only one thread
    //  at a time so synchronization (i.e. reentrancy) should not be
    //  a problem
    //

    if (dwOpenCount == 0) { // do this only on the first instance of the DLL
        // open Eventlog interface

        hEventLog = MonOpenEventLog();

        // open/create shared memory used by the application to pass performance values
        status = GetSharedMemoryDataHeader (
            &hAppMemSharedMemory, &hAppMemMutex, &pDataHeader,
            TRUE); // only read access is required

        if (status != ERROR_SUCCESS) {
            REPORT_ERROR_DATA (status, LOG_USER, NULL, 0);
            goto OpenExitPoint;
        }

        // get counter and help index base values from registry
        //      Open key to registry entry
        //      read First Counter and First Help values
        //      update static data strucutures by adding base to
        //          offset value in structure.

        status = RegOpenKeyEx (
            HKEY_LOCAL_MACHINE,
            "SYSTEM\\CurrentControlSet\\Services\\AppMem\\Performance",
            0L,
            KEY_ALL_ACCESS,
            &hKeyDriverPerf);

        if (status != ERROR_SUCCESS) {
            REPORT_ERROR_DATA (APPMEM_UNABLE_OPEN_DRIVER_KEY, LOG_USER,
                &status, sizeof(status));
            // this is fatal, if we can't get the base values of the
            // counter or help names, then the names won't be available
            // to the requesting application  so there's not much
            // point in continuing.
            goto OpenExitPoint;
        }

        size = sizeof (DWORD);
        status = RegQueryValueEx(
                    hKeyDriverPerf,
                    "First Counter",
                    0L,
                    &type,
                    (LPBYTE)&dwFirstCounter,
                    &size);

        if (status != ERROR_SUCCESS) {
            REPORT_ERROR_DATA (APPMEM_UNABLE_READ_FIRST_COUNTER, LOG_USER,
                &status, sizeof(status));
            // this is fatal, if we can't get the base values of the
            // counter or help names, then the names won't be available
            // to the requesting application  so there's not much
            // point in continuing.
            goto OpenExitPoint;
        }

        size = sizeof (DWORD);
        status = RegQueryValueEx(
                    hKeyDriverPerf,
                    "First Help",
                    0L,
                    &type,
                    (LPBYTE)&dwFirstHelp,
                    &size);

        if (status != ERROR_SUCCESS) {
            REPORT_ERROR_DATA (APPMEM_UNABLE_READ_FIRST_HELP, LOG_USER,
                &status, sizeof(status));
            // this is fatal, if we can't get the base values of the
            // counter or help names, then the names won't be available
            // to the requesting application  so there's not much
            // point in continuing.
            goto OpenExitPoint;
        }

        //
        //  NOTE: the initialization program could also retrieve
        //      LastCounter and LastHelp if they wanted to do
        //      bounds checking on the new number. e.g.
        //
        //      counter->CounterNameTitleIndex += dwFirstCounter;
        //      if (counter->CounterNameTitleIndex > dwLastCounter) {
        //          LogErrorToEventLog (INDEX_OUT_OF_BOUNDS);
        //      }

        AppMemDataDefinition.AppMemObjectType.ObjectNameTitleIndex += dwFirstCounter;
        AppMemDataDefinition.AppMemObjectType.ObjectHelpTitleIndex += dwFirstHelp;

        AppMemDataDefinition.AppMemBytesAllocated.CounterNameTitleIndex += dwFirstCounter;
        AppMemDataDefinition.AppMemBytesAllocated.CounterHelpTitleIndex += dwFirstHelp;
        AppMemDataDefinition.AppMemBytesAllocated.CounterOffset +=
            (DWORD)((LPBYTE)&ac.dwAppMemBytesAllocated - (LPBYTE)&ac);

        AppMemDataDefinition.AppMemAllocs.CounterNameTitleIndex += dwFirstCounter;
        AppMemDataDefinition.AppMemAllocs.CounterHelpTitleIndex += dwFirstHelp;
        AppMemDataDefinition.AppMemAllocs.CounterOffset =
            (DWORD)((LPBYTE)&ac.dwAppMemAllocs - (LPBYTE)&ac);

        AppMemDataDefinition.AppMemAllocsSec.CounterNameTitleIndex += dwFirstCounter;
        AppMemDataDefinition.AppMemAllocsSec.CounterHelpTitleIndex += dwFirstHelp;
        AppMemDataDefinition.AppMemAllocsSec.CounterOffset =
            (DWORD)((LPBYTE)&ac.dwAppMemAllocsSec - (LPBYTE)&ac);

        AppMemDataDefinition.AppMemReAllocs.CounterNameTitleIndex += dwFirstCounter;
        AppMemDataDefinition.AppMemReAllocs.CounterHelpTitleIndex += dwFirstCounter;
        AppMemDataDefinition.AppMemReAllocs.CounterOffset=
            (DWORD)((LPBYTE)&ac.dwAppMemReAllocs - (LPBYTE)&ac);

        AppMemDataDefinition.AppMemReAllocsSec.CounterNameTitleIndex += dwFirstCounter;
        AppMemDataDefinition.AppMemReAllocsSec.CounterHelpTitleIndex += dwFirstHelp;
        AppMemDataDefinition.AppMemReAllocsSec.CounterOffset =
            (DWORD)((LPBYTE)&ac.dwAppMemReAllocsSec - (LPBYTE)&ac);

        AppMemDataDefinition.AppMemFrees.CounterNameTitleIndex += dwFirstCounter;
        AppMemDataDefinition.AppMemFrees.CounterHelpTitleIndex += dwFirstHelp;
        AppMemDataDefinition.AppMemFrees.CounterOffset =
            (DWORD)((LPBYTE)&ac.dwAppMemFrees - (LPBYTE)&ac);

        AppMemDataDefinition.AppMemFreesSec.CounterNameTitleIndex += dwFirstCounter;
        AppMemDataDefinition.AppMemFreesSec.CounterHelpTitleIndex += dwFirstHelp;
        AppMemDataDefinition.AppMemFreesSec.CounterOffset =
            (DWORD)((LPBYTE)&ac.dwAppMemFreesSec - (LPBYTE)&ac);

        RegCloseKey (hKeyDriverPerf); // close key to registry

        bInitOK = TRUE; // ok to use this function
    }

    dwOpenCount++;  // increment OPEN counter

    status = ERROR_SUCCESS; // for successful exit

OpenExitPoint:

    return status;
}

DWORD APIENTRY
CollectAppMemPerformanceData(
    IN      LPWSTR  lpValueName,
    IN OUT  LPVOID  *lppData,
    IN OUT  LPDWORD lpcbTotalBytes,
    IN OUT  LPDWORD lpNumObjectTypes
)
/*++

Routine Description:

    This routine will return the data for the Application memory counters

Arguments:

   IN       LPWSTR   lpValueName
            pointer to a wide character string passed by registry.

   IN OUT   LPVOID   *lppData
         IN: pointer to the address of the buffer to receive the completed
            PerfDataBlock and subordinate structures. This routine will
            append its data to the buffer starting at the point referenced
            by *lppData.
         OUT: points to the first byte after the data structure added by this
            routine. This routine updated the value at lppdata after appending
            its data.

   IN OUT   LPDWORD  lpcbTotalBytes
         IN: the address of the DWORD that tells the size in bytes of the
            buffer referenced by the lppData argument
         OUT: the number of bytes added by this routine is writted to the
            DWORD pointed to by this argument

   IN OUT   LPDWORD  NumObjectTypes
         IN: the address of the DWORD to receive the number of objects added
            by this routine
         OUT: the number of objects added by this routine is writted to the
            DWORD pointed to by this argument

Return Value:

      ERROR_MORE_DATA if buffer passed is too small to hold data
         any error conditions encountered are reported to the event log if
         event logging is enabled.

      ERROR_SUCCESS  if success or any other error. Errors, however are
         also reported to the event log.

--*/
{
    //  Variables for reformating the data

    ULONG                       SpaceNeeded;
    PERF_INSTANCE_DEFINITION    *pPerfInstanceDef;
    APPMEM_DATA_DEFINITION      *pAppMemDataDefinition;
    DWORD                       dwQueryType;
    DWORD                       dwInstanceIndex;
    PAPPMEM_INSTANCE            pThisAppInstanceData = NULL;
    BOOL                        bFreeMutex;
    PAPPMEM_COUNTERS            pAC;

    //
    // before doing anything else, see if Open went OK
    //
    if ((!bInitOK) || (pDataHeader == NULL)) {
        // unable to continue because open failed.
        *lpcbTotalBytes = (DWORD) 0;
        *lpNumObjectTypes = (DWORD) 0;
        return ERROR_SUCCESS; // yes, this is a successful exit
    }

    // see if this is a foreign (i.e. non-NT) computer data request
    //
    dwQueryType = GetQueryType (lpValueName);

    if (dwQueryType == QUERY_FOREIGN) {
        // this routine does not service requests for data from
        // Non-NT computers
        *lpcbTotalBytes = (DWORD) 0;
        *lpNumObjectTypes = (DWORD) 0;
        return ERROR_SUCCESS;
    }

    if (dwQueryType == QUERY_ITEMS){
        if ( !(IsNumberInUnicodeList (AppMemDataDefinition.AppMemObjectType.ObjectNameTitleIndex, lpValueName))) {
            // request received for data object not provided by this routine
            *lpcbTotalBytes = (DWORD) 0;
            *lpNumObjectTypes = (DWORD) 0;
            return ERROR_SUCCESS;
        }
    }

    // if here, then this must be one for us so load data structures

    pAppMemDataDefinition = (APPMEM_DATA_DEFINITION *) *lppData;

    if (hAppMemMutex != NULL) {
        if (WaitForSingleObject (hAppMemMutex, SHARED_MEMORY_MUTEX_TIMEOUT) == WAIT_FAILED) {
            // unable to obtain a lock
            bFreeMutex = FALSE;
        } else {
            bFreeMutex = TRUE;
        }
    } else {
        bFreeMutex = FALSE;
    }

    // always return an "instance sized" buffer after the definition blocks
    // to prevent perfmon from reading bogus data. This is strictly a hack
    // to accomodate how PERFMON handles the "0" instance case.
    //  By doing this, perfmon won't choke when there are no instances
    //  and the counter object & counters will be displayed in the
    //  list boxes, even though no instances will be listed.

    SpaceNeeded = sizeof(APPMEM_DATA_DEFINITION) +
        ((pDataHeader->dwInstanceCount > 0 ? pDataHeader->dwInstanceCount : 1) * (
            sizeof(PERF_INSTANCE_DEFINITION) +
            MAX_SIZEOF_INSTANCE_NAME +
            sizeof(APPMEM_COUNTERS)));

    if ( *lpcbTotalBytes < SpaceNeeded ) {
        // not enough room so return nothing.
        *lpcbTotalBytes = (DWORD) 0;
        *lpNumObjectTypes = (DWORD) 0;
        return ERROR_MORE_DATA;
    }

    // copy the object & counter definition information

    memmove(pAppMemDataDefinition,
        &AppMemDataDefinition,
        sizeof(APPMEM_DATA_DEFINITION));

    // prepare to read the instance data
    pPerfInstanceDef = (PERF_INSTANCE_DEFINITION *) &pAppMemDataDefinition[1];

    // point to the first instance structure in the shared buffer
    pThisAppInstanceData = FIRST_INUSE(pDataHeader);

    // process each of the instances in the shared memory buffer
    dwInstanceIndex = 0;
    while ((dwInstanceIndex < pDataHeader->dwInstanceCount) &&
            (pThisAppInstanceData != NULL)){
        // initialize this instance
        MonBuildInstanceDefinition (
            pPerfInstanceDef,
            &pAC, // pointer to first byte after instance def
            0,  // no parent
            0,  //  object to reference
            (DWORD)PERF_NO_UNIQUE_ID,
            pThisAppInstanceData->wcszInstanceName);
        //
        //    collect and format app memory perf data from shared memory
        //

        pAC->CounterBlock.ByteLength = sizeof(APPMEM_COUNTERS);

        // set pointer to first counter data field

        pAC->dwAppMemBytesAllocated = pThisAppInstanceData->dwApplicationBytes;
        pAC->dwAppMemAllocs = pThisAppInstanceData->dwAllocCalls;
        pAC->dwAppMemAllocsSec = pThisAppInstanceData->dwAllocCalls;
        pAC->dwAppMemReAllocs = pThisAppInstanceData->dwReAllocCalls;
        pAC->dwAppMemReAllocsSec = pThisAppInstanceData->dwReAllocCalls;
        pAC->dwAppMemFrees = pThisAppInstanceData->dwFreeCalls;
        pAC->dwAppMemFreesSec = pThisAppInstanceData->dwFreeCalls;

        // setup for the next instance
        dwInstanceIndex++;
        pThisAppInstanceData =
            APPMEM_INST(pDataHeader, pThisAppInstanceData->dwOffsetOfNext);
        pPerfInstanceDef =
            (PERF_INSTANCE_DEFINITION *)((LPBYTE)pAC + sizeof(APPMEM_COUNTERS));
    }

    if (dwInstanceIndex == 0) {
        // zero fill one instance sized block of data if there are no
        // data instances

        memset (pPerfInstanceDef, 0,
            (sizeof(PERF_INSTANCE_DEFINITION) +
            MAX_SIZEOF_INSTANCE_NAME +
            sizeof(APPMEM_COUNTERS)));

        // adjust pointer to point to end of zeroed block
        (BYTE *)pPerfInstanceDef += (sizeof(PERF_INSTANCE_DEFINITION) +
            MAX_SIZEOF_INSTANCE_NAME +
            sizeof(APPMEM_COUNTERS));

    }

    // done with the shared memory so free the mutex if one was
    // acquired

    if (bFreeMutex) {
        ReleaseMutex (hAppMemMutex);
    }

    *lppData = (PVOID)pPerfInstanceDef;

    // update arguments for return

    *lpNumObjectTypes = 1;
    pAppMemDataDefinition->AppMemObjectType.NumInstances = dwInstanceIndex;

    pAppMemDataDefinition->AppMemObjectType.TotalByteLength =
    *lpcbTotalBytes = (DWORD)((PBYTE)pPerfInstanceDef -
                              (PBYTE) pAppMemDataDefinition);

    return ERROR_SUCCESS;
}

DWORD APIENTRY
CloseAppMemPerformanceData(
)

/*++

Routine Description:

    This routine closes the open handles to Application Memory
        usage performance counters

Arguments:

    None.

Return Value:

    ERROR_SUCCESS

--*/

{
    if ((--dwOpenCount) == 0) { // when this is the last thread...

        if (hAppMemSharedMemory != NULL) CloseHandle(hAppMemSharedMemory);
        if (hAppMemMutex != NULL)       CloseHandle(hAppMemMutex);

        pDataHeader = NULL;

        MonCloseEventLog();
    }

    return ERROR_SUCCESS;

}


PERFMSG.H

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright (c) 1992 Microsoft Corporation

Module Name:

    perfmsg.h

Abstract:

    This file provides the macros and definitions used by the extensible
    counters for reporting events to the event logging facility

Author:

      Bob Watson (a-robw) 5 Oct 92

Revision History:

--*/
#ifndef  _PERFMSG_H_
#define  _PERFMSG_H_
//
// Report error message ID's for Counters
//

#define APP_NAME  "appmemry"

//
// The constant below defines how many (if any) messages will be reported
// to the event logger. As the number goes up in value more and more events
// will be reported. The purpose of this is to allow lots of messages during
// development and debugging (e.g. a message level of 3) to a minimum of
// messages (e.g. operational messages with a level of 1) or no messages if
// message logging inflicts too much of a performance penalty. Right now
// this is a compile time constant, but could later become a registry entry.
//
//    Levels:  LOG_NONE = No event log messages ever
//             LOG_USER = User event log messages (e.g. errors)
//             LOG_DEBUG = Minimum Debugging
//             LOG_VERBOSE = Maximum Debugging
//

#define  LOG_NONE     0
#define  LOG_USER     1
#define  LOG_DEBUG    2
#define  LOG_VERBOSE  3

#define  MESSAGE_LEVEL_DEFAULT  LOG_USER

// define macros
//
// Format for event log calls without corresponding insertion strings is:
//    REPORT_xxx (message_value, message_level)
//       where:
//          xxx is the severity to be displayed in the event log
//          message_value is the numeric ID from above
//          message_level is the "filtering" level of error reporting
//             using the error levels above.
//
// if the message has a corresponding insertion string whose symbol conforms
// to the format CONSTANT = numeric value and CONSTANT_S = string constant for
// that message, then the
//
//    REPORT_xxx_STRING (message_value, message_level)
//
// macro may be used.
//

//
// REPORT_SUCCESS was intended to show Success in the error log, rather it
// shows "N/A" so for now it's the same as information, though it could
// (should) be changed  in the future
//

#define REPORT_SUCCESS(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \
   0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)

#define REPORT_INFORMATION(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \
   0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)

#define REPORT_WARNING(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, \
   0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)

#define REPORT_ERROR(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, \
   0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)

#define REPORT_INFORMATION_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \
   0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE)

#define REPORT_WARNING_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, \
   0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE)

#define REPORT_ERROR_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, \
   0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE)

// External Variables

extern HANDLE hEventLog;   // handle to event log
extern DWORD  dwLogUsers;  // counter of event log using routines
extern DWORD  MESSAGE_LEVEL; // event logging detail level

#endif //_PERFMSG_H_


PERFUTIL.C

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright (c) 1992  Microsoft Corporation

Module Name:

    perfutil.c

Abstract:

    This file implements the utility routines used to construct the
common parts of a PERF_INSTANCE_DEFINITION (see winperf.h) and
    perform event logging functions.

Created:

    Russ Blake  07/30/92

Revision History:

--*/
//
//  include files
//
//#include <nt.h>
//#include <ntrtl.h>
//#include <nturtl.h>
#include <windows.h>
#include <string.h>
#include <winperf.h>
#include "memctrs.h" // error message definition
#include "perfmsg.h"
#include "perfutil.h"

#define INITIAL_SIZE     1024L
#define EXTEND_SIZE      1024L

//
// Global data definitions.
//

ULONG                   ulInfoBufferSize = 0;

HANDLE hEventLog = NULL;      // event log handle for reporting events
                              // initialized in Open... routines
DWORD  dwLogUsers = 0;        // count of functions using event log

DWORD MESSAGE_LEVEL = 0;

WCHAR GLOBAL_STRING[] = L"Global";
WCHAR FOREIGN_STRING[] = L"Foreign";
WCHAR COSTLY_STRING[] = L"Costly";

WCHAR NULL_STRING[] = L"\0";    // pointer to null string

// test for delimiter, end of line and non-digit characters
// used by IsNumberInUnicodeList routine
//
#define DIGIT       1
#define DELIMITER   2
#define INVALID     3

#define EvalThisChar(c,d) ( \
     (c == d) ? DELIMITER : \
     (c == 0) ? DELIMITER : \
     (c < (WCHAR)'0') ? INVALID : \
     (c > (WCHAR)'9') ? INVALID : \
     DIGIT)

BOOL
MonBuildInstanceDefinition(
    PERF_INSTANCE_DEFINITION *pBuffer,
    PVOID *pBufferNext,
    DWORD ParentObjectTitleIndex,
    DWORD ParentObjectInstance,
    DWORD UniqueID,
    LPWSTR  Name
    )
/*++

    MonBuildInstanceDefinition  -   Build an instance of an object

        Inputs:

            pBuffer         -   pointer to buffer where instance is to
                                be constructed

            pBufferNext     -   pointer to a pointer which will contain
                                next available location, DWORD aligned

            ParentObjectTitleIndex
                            -   Title Index of parent object type; 0 if
                                no parent object

            ParentObjectInstance
                            -   Index into instances of parent object
                                type, starting at 0, for this instances
                                parent object instance

            UniqueID        -   a unique identifier which should be used
                                instead of the Name for identifying
                                this instance

            Name            -   Name of this instance
--*/
{
    DWORD NameLength = 0;
    WCHAR *pName;

    //
    //  Include trailing null in name size
    //

    NameLength = lstrlenW(Name) * sizeof(WCHAR);
    if (NameLength > 0) {
        NameLength += sizeof(WCHAR); // to include the terminating NULL
    }

    pBuffer->ByteLength = sizeof(PERF_INSTANCE_DEFINITION) +
                          DWORD_MULTIPLE(NameLength);

    pBuffer->ParentObjectTitleIndex = ParentObjectTitleIndex;
    pBuffer->ParentObjectInstance = ParentObjectInstance;
    pBuffer->UniqueID = UniqueID;
    pBuffer->NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
    pBuffer->NameLength = NameLength;

    pName = (PWCHAR)&pBuffer[1];
    lstrcpyW (pName, Name);

    *pBufferNext = (PVOID) ((PCHAR) pBuffer + pBuffer->ByteLength);
    return TRUE;
}

HANDLE
MonOpenEventLog (
)
/*++

Routine Description:

    Reads the level of event logging from the registry and opens the
        channel to the event logger for subsequent event log entries.

Arguments:

      None

Return Value:

    Handle to the event log for reporting events.
    NULL if open not successful.

--*/
{
    HKEY hAppKey;
    TCHAR LogLevelKeyName[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib";
    TCHAR LogLevelValueName[] = "EventLogLevel";

    LONG lStatus;

    DWORD dwLogLevel;
    DWORD dwValueType;
    DWORD dwValueSize;

    // if global value of the logging level not initialized or is disabled,
    //  check the registry to see if it should be updated.

    if (!MESSAGE_LEVEL) {

       lStatus = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                               LogLevelKeyName,
                               0,
                               KEY_READ,
                               &hAppKey);

       dwValueSize = sizeof (dwLogLevel);

       if (lStatus == ERROR_SUCCESS) {
            lStatus = RegQueryValueEx (hAppKey,
                               LogLevelValueName,
                               (LPDWORD)NULL,
                               &dwValueType,
                               (LPBYTE)&dwLogLevel,
                               &dwValueSize);

            if (lStatus == ERROR_SUCCESS) {
               MESSAGE_LEVEL = dwLogLevel;
            } else {
               MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
            }
            RegCloseKey (hAppKey);
       } else {
         MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
       }
    }

    if (hEventLog == NULL){
         hEventLog = RegisterEventSource (
            (LPTSTR)NULL,            // Use Local Machine
            APP_NAME);               // event log app name to find in registry

         if (hEventLog != NULL) {
            REPORT_INFORMATION (UTIL_LOG_OPEN, LOG_DEBUG);
         }
    }

    if (hEventLog != NULL) {
         dwLogUsers++;           // increment count of perfctr log users
    }
    return (hEventLog);
}

VOID
MonCloseEventLog (
)
/*++

Routine Description:

      Closes the handle to the event logger if this is the last caller

Arguments:

      None

Return Value:

      None

--*/
{
    if (hEventLog != NULL) {
        dwLogUsers--;         // decrement usage
        if (dwLogUsers <= 0) {    // and if we're the last, then close up log
            REPORT_INFORMATION (UTIL_CLOSING_LOG, LOG_DEBUG);
            DeregisterEventSource (hEventLog);
        }
    }
}

DWORD
GetQueryType (
    IN LPWSTR lpValue
)
/*++

GetQueryType

    returns the type of query described in the lpValue string so that
    the appropriate processing method may be used

Arguments

    IN lpValue
        string passed to PerfRegQuery Value for processing

Return Value

    QUERY_GLOBAL
        if lpValue == 0 (null pointer)
           lpValue == pointer to Null string
           lpValue == pointer to "Global" string

    QUERY_FOREIGN
        if lpValue == pointer to "Foriegn" string

    QUERY_COSTLY
        if lpValue == pointer to "Costly" string

    otherwise:

    QUERY_ITEMS

--*/
{
    WCHAR   *pwcArgChar, *pwcTypeChar;
    BOOL    bFound;

    if (lpValue == 0) {
        return QUERY_GLOBAL;
    } else if (*lpValue == 0) {
        return QUERY_GLOBAL;
    }

    // check for "Global" request

    pwcArgChar = lpValue;
    pwcTypeChar = GLOBAL_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string

    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_GLOBAL;

    // check for "Foreign" request

    pwcArgChar = lpValue;
    pwcTypeChar = FOREIGN_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string

    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_FOREIGN;

    // check for "Costly" request

    pwcArgChar = lpValue;
    pwcTypeChar = COSTLY_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string

    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_COSTLY;

    // if not Global and not Foreign and not Costly,
    // then it must be an item list

    return QUERY_ITEMS;

}

BOOL
IsNumberInUnicodeList (
    IN DWORD   dwNumber,
    IN LPWSTR  lpwszUnicodeList
)
/*++

IsNumberInUnicodeList

Arguments:

    IN dwNumber
        DWORD number to find in list

    IN lpwszUnicodeList
        Null terminated, Space delimited list of decimal numbers

Return Value:

    TRUE:
            dwNumber was found in the list of unicode number strings

    FALSE:
            dwNumber was not found in the list.

--*/
{
    DWORD   dwThisNumber;
    WCHAR   *pwcThisChar;
    BOOL    bValidNumber;
    BOOL    bNewItem;
    WCHAR   wcDelimiter;    // could be an argument to be more flexible

    if (lpwszUnicodeList == 0) return FALSE;    // null pointer, # not founde

    pwcThisChar = lpwszUnicodeList;
    dwThisNumber = 0;
    wcDelimiter = (WCHAR)' ';
    bValidNumber = FALSE;
    bNewItem = TRUE;

    while (TRUE) {
        switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
            case DIGIT:
                // if this is the first digit after a delimiter, then
                // set flags to start computing the new number
                if (bNewItem) {
                    bNewItem = FALSE;
                    bValidNumber = TRUE;
                }
                if (bValidNumber) {
                    dwThisNumber *= 10;
                    dwThisNumber += (*pwcThisChar - (WCHAR)'0');
                }
                break;

            case DELIMITER:
                // a delimter is either the delimiter character or the
                // end of the string ('\0') if when the delimiter has been
                // reached a valid number was found, then compare it to the
                // number from the argument list. if this is the end of the
                // string and no match was found, then return.
                //
                if (bValidNumber) {
                    if (dwThisNumber == dwNumber) return TRUE;
                    bValidNumber = FALSE;
                }
                if (*pwcThisChar == 0) {
                    return FALSE;
                } else {
                    bNewItem = TRUE;
                    dwThisNumber = 0;
                }
                break;

            case INVALID:
                // if an invalid character was encountered, ignore all
                // characters up to the next delimiter and then start fresh.
                // the invalid number is not compared.
                bValidNumber = FALSE;
                break;

            default:
                break;

        }
        pwcThisChar++;
    }

}   // IsNumberInUnicodeList


PERFUTIL.H

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright (c) 1992 Microsoft Corporation

Module Name:

    perfutil.h

Abstract:

    This file supports routines used to parse and
    create Performance Monitor Data Structures.
    It actually supports Performance Object types with
    multiple instances

Author:

    Russ Blake  7/30/92

Revision History:

--*/
#ifndef _PERFUTIL_H_
#define _PERFUTIL_H_

//  Utility macro.  This is used to reserve a DWORD multiple of
//  bytes for Unicode strings embedded in the definitional data,
//  viz., object instance names.
//
#define DWORD_MULTIPLE(x) (((x+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD))

//    (assumes dword is 4 bytes long and pointer is a dword in size)
#define ALIGN_ON_DWORD(x) ((VOID *)( ((DWORD) x & 0x00000003) ? ( ((DWORD) x & 0xFFFFFFFC) + 4 ) : ( (DWORD) x ) ))

extern WCHAR  GLOBAL_STRING[];      // Global command (get all local ctrs)
extern WCHAR  FOREIGN_STRING[];           // get data from foreign computers
extern WCHAR  COSTLY_STRING[];
extern WCHAR  NULL_STRING[];

#define QUERY_GLOBAL    1
#define QUERY_ITEMS     2
#define QUERY_FOREIGN   3
#define QUERY_COSTLY    4

//
// The definition of the only routine of perfutil.c, It builds part of a
// performance data instance (PERF_INSTANCE_DEFINITION) as described in
// winperf.h
//

// from PerfUtil.C

HANDLE MonOpenEventLog ();
VOID MonCloseEventLog ();
DWORD GetQueryType (IN LPWSTR);
BOOL IsNumberInUnicodeList (DWORD, LPWSTR);
BOOL MonBuildInstanceDefinition(
    PERF_INSTANCE_DEFINITION *pBuffer,
    PVOID *pBufferNext,
    DWORD ParentObjectTitleIndex,
    DWORD ParentObjectInstance,
    DWORD UniqueID,
    LPWSTR  Name
);

#endif  //_PERFUTIL_H_


SHAREMEM.C

/*++

    Copyright (c) 1996 -- Microsoft Corporation.

    sharemem.c

    shared memory functions used by both application and performance
    dlls

--*/
//
//  Include Files
//
#include <windows.h>
#include <string.h>
#include <winperf.h>
#include "..\inc\appmemi.h"     // definitions shared with the app. dll
#include "memctrs.h"            // error message definition

LONG
_stdcall
GetSharedMemoryDataHeader (
    IN  HANDLE              *phAppMemSharedMemory,
    IN  HANDLE              *phAppMemMutex,
    IN  PAPPMEM_DATA_HEADER *pHeader,
    IN  BOOL                bReadOnlyAccess
)
{
    LONG    Status, MemStatus;
    TCHAR   szMappedObject[] = SHARED_MEMORY_OBJECT_NAME;
    BOOL    bFreeMutex;
    PAPPMEM_INSTANCE    pLastItem;
    PAPPMEM_INSTANCE    pAppData;
    DWORD   dwInstance;

    SetLastError (ERROR_SUCCESS);   // just to clear it out

    *phAppMemSharedMemory = CreateFileMapping(
                    (HANDLE)0xFFFFFFFF,     // to create a temp file
                    NULL,                   // no security
                    PAGE_READWRITE,         // to allow read & write access
                    0,
    SHARED_MEMORY_OBJECT_SIZE,  // file size
    szMappedObject);        // object name

    MemStatus = GetLastError();    // to see if this is the first opening

    // return error if unsuccessful
    if (*phAppMemSharedMemory == NULL) {
        Status = APPMEM_OPEN_FILE_MAPPING_ERROR;
        // this is fatal, if we can't get data then there's no
        // point in continuing.
    } else {
        // the application memory file was created/access successfully
        // so we need to get the sync. mutex before we use it
        *phAppMemMutex = CreateMutex (NULL, FALSE, SHARED_MEMORY_MUTEX_NAME);

        // if successful then wait for ownership, otherwise,
        // we'll just take our chances.

        if (*phAppMemMutex != NULL) {
            if (WaitForSingleObject (*phAppMemMutex,
                SHARED_MEMORY_MUTEX_TIMEOUT) == WAIT_FAILED) {
                // unable to obtain a lock
                bFreeMutex = FALSE;
            } else {
                bFreeMutex = TRUE;
            }
        } else {
            bFreeMutex = FALSE;
        }

        if (MemStatus != ERROR_ALREADY_EXISTS) {
            // this is the first access to the file so initialize the
            // instance count
        *pHeader = (PAPPMEM_DATA_HEADER)MapViewOfFile(
                            *phAppMemSharedMemory,  // shared mem handle
            FILE_MAP_WRITE,         // access desired
            0,                      // starting offset
            0,
            0);                     // map the entire object
            if (*pHeader != NULL) {
                // if here, then pdwInstanceCount should be valid
                // so initialize the shared memory structure
                // clear memory block
                memset (*pHeader, 0, SHARED_MEMORY_OBJECT_SIZE);

                // initialize application data header
                (*pHeader)->dwInstanceCount = 0;       // no instances
                (*pHeader)->dwFirstInUseOffset = 0;    // no busy items yet
                (*pHeader)->dwFirstFreeOffset =
                    (DWORD)((LPBYTE)&(*pHeader)[1] -
                            (LPBYTE)*pHeader);

                pLastItem = FIRST_FREE((*pHeader));

                // set pointers in each item of block
                pAppData = pLastItem + 1;
                for (dwInstance = (SHARED_MEMORY_ITEM_COUNT -1); dwInstance > 0; dwInstance--) {
                    pLastItem->dwOffsetOfNext = (DWORD)((LPBYTE)pAppData -
                                                (LPBYTE)(*pHeader));
                    pLastItem = pAppData;
                    pAppData++;
                }
                pAppData->dwOffsetOfNext = 0; // end of list
                Status = ERROR_SUCCESS;
            } else {
                Status = APPMEM_UNABLE_MAP_VIEW_OF_FILE;
            }
        } else {
            // the status is ERROR_ALREADY_EXISTS which is successful
            Status = ERROR_SUCCESS;
        }
        // see if Read Only access is required
        if (Status == ERROR_SUCCESS) {
            // by now the shared memory has already been initialized so
            // we if we don't need write access any more or if it has
            // already been opened, then open with the desired access
        *pHeader = (PAPPMEM_DATA_HEADER)MapViewOfFile(
                            *phAppMemSharedMemory,  // shared mem handle
            (bReadOnlyAccess ? FILE_MAP_READ :
            FILE_MAP_WRITE),    // access desired
            0,                      // starting offset
            0,
            0);                     // map the entire object
        if (*pHeader == NULL) {
                Status = APPMEM_UNABLE_MAP_VIEW_OF_FILE;
                // this is fatal, if we can't get data then there's no
                // point in continuing.
        } else {
                Status = ERROR_SUCCESS;
            }
        }

        // done with the shared memory so free the mutex if one was
        // acquired

        if (bFreeMutex) {
            ReleaseMutex (*phAppMemMutex);
        }
    }
    return Status;
}


APPMEM.H

/*++

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    appmem.h

Abstract:

    function prototypes and definitions for applications using the
    application memory performance counters

Revision History

    30 Aug 1995     Bob Watson (a-robw)     Created

--*/
#ifndef _APPMEM_H_
#define _APPMEM_H_

#ifdef __cplusplus
extern "C" {
#endif

HGLOBAL
GlobalAllocP (
    UINT    fuFlags,
    DWORD   cbBytes
);

HGLOBAL
GlobalFreeP (
    HGLOBAL hglbMem
);

HGLOBAL
GlobalReAllocP (
    HGLOBAL hglbMem,
    DWORD   cbBytes,
    UINT    fuFlags
);

#ifdef __cplusplus
}
#endif

#endif // _APPMEM_H_


MEMCTRNM.H

//
//  memctrnm.h
//
//  Offset definition file for exensible counter objects and counters
//
//  These "relative" offsets must start at 0 and be multiples of 2 (i.e.
//  even numbers). In the Open Procedure, they will be added to the
//  "First Counter" and "First Help" values fo the device they belong to,
//  in order to determine the  absolute location of the counter and
//  object names and corresponding help text in the registry.
//
//  this file is used by the extensible counter DLL code as well as the
//  counter name and help text definition file (.INI) file that is used
//  by LODCTR to load the names into the registry.
//
#define APPMEMOBJ       0
#define APPMEMALLOC     2
#define APPALLOCS       4
#define APPALLOCSSEC    6
#define APPREALLOCS     8
#define APPREALLOCSSEC  10
#define APPFREES        12
#define APPFREESSEC     14


README.TXT

LeakyBin WinNT-based Application

LeakyBin is a sample application the demonstrates the use of
application performance counters.

The files included in this sample are:

LEAKYBIN.C - The .C source file
LEAKYBIN.H - The .H file for the sample
RESOURCE.H - The .H file created by VC++ 2.0
LEAKYBIN.DEF - The definition file
LEAKYBIN.RC - Contains resource information such as menus and dialog boxes
LEAKYBIN.HPJ - The Help file source
LEAKYBIN.CNT - The contents list for the Help file
LEAKYBIN.RTF -  A rich text format file used for Help
MAKEFILE - A command line make file
LEAKYBIN.ICO - The icon for the LEAKYBIN sample
SMALL.ICO - The small-icon for the LEAKYBIN sample
LEAKYBIN.BMP - A bitmap for the LEAKYBIN sample


LEAKYBIN.C

//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1996 - 1997 Microsoft Corporation.  All Rights Reserved.
//
// PROGRAM: Leakybin.c
//
// PURPOSE: Illustrates the use of Windows NT application performance
//              counters to measure GlobalAlloc usage
//
// PLATFORMS:  Windows NT only
//
// FUNCTIONS:
//    WinMain() - calls initialization function, processes message loop
//    InitApplication() - Initializes window data nd registers window
//    InitInstance() -saves instance handle and creates main window
//    WindProc() Processes messages
//    About() - Process menssages for "About" dialog box
//    MyRegisterClass() - Registers the application's window class
//    CenterWindow() -  Centers one window over another
//
// SPECIAL INSTRUCTIONS: N/A
//
#define APPNAME "Leakybin"

// Windows Header Files:
#include <windows.h>

// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <stdio.h>

// Local Header Files
#include "leakybin.h"

// Makes it easier to determine appropriate code paths:
#define IS_WIN32    TRUE
#define IS_NT       IS_WIN32 && (BOOL)(GetVersion() < 0x80000000)

// Global Variables:

HINSTANCE hInst;      // current instance
char szAppName[100];  // Name of the app
char szTitle[100];    // The title bar text

static int      TimerID = 0;
static BOOL     TimerRunning = FALSE;
static HMENU    hAppMenu, hTestMenu;
static MEMORY_ALLOC_BLOCK   mabListHead = {NULL};

// Foward declarations of functions included in this code module:

ATOM MyRegisterClass(CONST WNDCLASS*);
BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
BOOL CenterWindow (HWND, HWND);
LPTSTR   GetStringRes (int id);

//
//  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
//
//  PURPOSE: Entry point for the application.
//
//  COMMENTS:
//
// This function initializes the application and processes the
// message loop.
//
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
   MSG msg;
   HANDLE hAccelTable;

   // Initialize global strings
   lstrcpy (szAppName, APPNAME);
   LoadString (hInstance, IDS_APP_TITLE, szTitle, 100);

   if (!hPrevInstance) {
      // Perform instance initialization:
      if (!InitApplication(hInstance)) {
         return (FALSE);
      }
   }

   // Perform application initialization:
   if (!InitInstance(hInstance, nCmdShow)) {
      return (FALSE);
   }

   hAccelTable = LoadAccelerators (hInstance, szAppName);

   // Main message loop:
   while (GetMessage(&msg, NULL, 0, 0)) {
      if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg)) {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
   }

   return (msg.wParam);

   lpCmdLine; // This will prevent 'unused formal parameter' warnings
}

//
//  FUNCTION: MyRegisterClass(CONST WNDCLASS*)
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage is only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(CONST WNDCLASS *lpwc)
{
   HANDLE  hMod;
   FARPROC proc;
   WNDCLASSEX wcex;

   hMod = GetModuleHandle ("USER32");
   if (hMod != NULL) {

#if defined (UNICODE)
      proc = GetProcAddress (hMod, "RegisterClassExW");
#else
      proc = GetProcAddress (hMod, "RegisterClassExA");
#endif

      if (proc != NULL) {

         wcex.style         = lpwc->style;
         wcex.lpfnWndProc   = lpwc->lpfnWndProc;
         wcex.cbClsExtra    = lpwc->cbClsExtra;
         wcex.cbWndExtra    = lpwc->cbWndExtra;
         wcex.hInstance     = lpwc->hInstance;
         wcex.hIcon         = lpwc->hIcon;
         wcex.hCursor       = lpwc->hCursor;
         wcex.hbrBackground = lpwc->hbrBackground;
                     wcex.lpszMenuName  = lpwc->lpszMenuName;
         wcex.lpszClassName = lpwc->lpszClassName;

         // Added elements for Windows 95:
         wcex.cbSize = sizeof(WNDCLASSEX);
         wcex.hIconSm = LoadIcon(wcex.hInstance, "SMALL");

         return (*proc)(&wcex);//return RegisterClassEx(&wcex);
      }
   }
   return (RegisterClass(lpwc));
}

//
//  FUNCTION: InitApplication(HANDLE)
//
//  PURPOSE: Initializes window data and registers window class
//
//  COMMENTS:
//
//       In this function, we initialize a window class by filling out a data
//       structure of type WNDCLASS and calling either RegisterClass or
//       the internal MyRegisterClass.
//
BOOL InitApplication(HINSTANCE hInstance)
{
    WNDCLASS  wc;

    // Fill in window class structure with parameters that describe
    // the main window.
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = (WNDPROC)WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon (hInstance, szAppName);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName  = szAppName;
    wc.lpszClassName = szAppName;

    // Register the window class and return success/failure code.
    return RegisterClass(&wc);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szAppName, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
      NULL, NULL, hInstance, NULL);

   if (!hWnd) {
      return (FALSE);
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return (TRUE);
}
void FreeAllocatedMemory()
{
    {
    PMEMORY_ALLOC_BLOCKpNextMab, pMab;

    pMab = mabListHead.pNext;

    while (pMab != NULL) {
    pNextMab = pMab->pNext;
    G_FREE (pMab);
    pMab = pNextMab;
    }

    mabListHead.pNext = NULL;
    }
}
//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  MESSAGES:
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//    WM_DISPLAYCHANGE - message sent to Plug & Play systems when the display changes
//    WM_RBUTTONDOWN - Right mouse click -- put up context menu here if appropriate
//    WM_NCRBUTTONUP - User has clicked the right button on the application's system menu
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   int wmId, wmEvent;
   PAINTSTRUCT ps;
   HDC hdc;
      POINT pnt;
   HMENU hMenu;
      BOOL bGotHelp;

   switch (message) {
      case WM_CREATE:
        // clear timer flags
        TimerID = 0;
        TimerRunning = FALSE;

        // enable "Start" menu selection
        hAppMenu = GetMenu (hWnd);
        hTestMenu  = GetSubMenu (hAppMenu, 1);
        EnableMenuItem (hTestMenu, IDM_STOP, MF_BYCOMMAND | MF_GRAYED);
        EnableMenuItem (hTestMenu, IDM_START, MF_BYCOMMAND | MF_ENABLED);
        break;

      case WM_COMMAND:
         wmId    = LOWORD(wParam); // Remember, these are...
         wmEvent = HIWORD(wParam); // ...different for Win32!

         //Parse the menu selections:
         switch (wmId) {

            case IDM_EXIT:
               DestroyWindow (hWnd);
               break;

            case IDM_START:
                if (!TimerRunning) {
                    TimerID = SetTimer (hWnd, LEAK_TIMER, TIME_INTERVAL, NULL);
                    if (TimerID != 0) {
                        TimerRunning = TRUE;
                        EnableMenuItem (hTestMenu, IDM_START,
                            MF_BYCOMMAND | MF_GRAYED);
                        EnableMenuItem (hTestMenu, IDM_STOP,
                            MF_BYCOMMAND | MF_ENABLED);
                    } else {
                        //unable to start timer
                        MessageBeep (MB_ICONEXCLAMATION);
                    }
                }
                InvalidateRect (hWnd, NULL, TRUE);
                break;

            case IDM_STOP:
                if (TimerRunning) {
                    KillTimer (hWnd, LEAK_TIMER);
                    TimerID = 0;
                    TimerRunning = FALSE;
                    EnableMenuItem (hTestMenu, IDM_STOP,
                        MF_BYCOMMAND | MF_GRAYED);
                    EnableMenuItem (hTestMenu, IDM_START,
                        MF_BYCOMMAND | MF_ENABLED);
                }
                InvalidateRect (hWnd, NULL, TRUE);
                break;

            case IDM_RESET:
                FreeAllocatedMemory();
                InvalidateRect (hWnd, NULL, TRUE);
                break;

            case IDM_ABOUT:
               DialogBox(hInst, "AboutBox", hWnd, (DLGPROC)About);
               break;

            case IDM_HELPTOPICS: // Only called in Windows 95
               bGotHelp = WinHelp (hWnd, APPNAME".HLP", HELP_FINDER,(DWORD)0);
               if (!bGotHelp)
               {
                  MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP),
                              szAppName, MB_OK|MB_ICONHAND);
               }
               break;

            default:
               return (DefWindowProc(hWnd, message, wParam, lParam));
         }
         break;

      case WM_TIMER:
        {
        PMEMORY_ALLOC_BLOCKpMab, pNewMab;

        pNewMab = (PMEMORY_ALLOC_BLOCK)G_ALLOC (GPTR, ALLOCATION_SIZE);

        if (pNewMab != NULL) {
        // save this pointer
        pNewMab->pNext = NULL;
        if (mabListHead.pNext == NULL) {
        // this is the first entry
        mabListHead.pNext = pNewMab;
        } else {
        // go to end of list
        pMab = mabListHead.pNext;
        while (pMab->pNext != NULL) pMab = pMab->pNext;
        pMab->pNext = pNewMab;
        }
                InvalidateRect (hWnd, NULL, TRUE);
        }
        }
        break;

      case WM_RBUTTONDOWN: // RightClick in windows client area...
            pnt.x = LOWORD(lParam);
            pnt.y = HIWORD(lParam);
            ClientToScreen(hWnd, (LPPOINT) &pnt);
      // This is where you would determine the appropriate 'context'
      // menu to bring up. Since this app has no real functionality,
      // we will just bring up the 'Help' menu:
            hMenu = GetSubMenu (GetMenu (hWnd), 2);
            if (hMenu) {
                TrackPopupMenu (hMenu, 0, pnt.x, pnt.y, 0, hWnd, NULL);
            } else {
            // Couldn't find the menu...
                MessageBeep(0);
            }
            break;

      case WM_DISPLAYCHANGE: // Only comes through on plug'n'play systems
      {
         SIZE  szScreen;
         DWORD dwBitsPerPixel = (DWORD)wParam;

         szScreen.cx = LOWORD(lParam);
         szScreen.cy = HIWORD(lParam);

         MessageBox (GetFocus(), GetStringRes(IDS_DISPLAYCHANGED),
                     szAppName, 0);
      }
      break;

      case WM_PAINT:
        {
        MEMORYSTATUSMemoryStatusData;
            LONGLONGllInUse;
        DWORDdwPercentUsed;

            int     nX, nY;
            LONG    lTextOutReturn;
            int     nStringLength;
            CHAR            szOutputString[100];

            hdc = BeginPaint (hWnd, &ps);
            // Add any drawing code here...
        GlobalMemoryStatus (&MemoryStatusData);

        llInUse = (LONGLONG)(MemoryStatusData.dwTotalPageFile -
                MemoryStatusData.dwAvailPageFile + 5 );
        llInUse *= 1000;
        llInUse /= MemoryStatusData.dwTotalPageFile;
        llInUse /= 10;

        dwPercentUsed = (DWORD)llInUse;

            nX = 0;
            nY = 0;
            sprintf (szOutputString, "Reported Memory Load: \t%3.1d%%",
                MemoryStatusData.dwMemoryLoad);
            nStringLength = lstrlen (szOutputString) * sizeof (CHAR);
            lTextOutReturn = TabbedTextOut (hdc, nX, nY,
                szOutputString, nStringLength, 0, NULL, 0);
            nY += HIWORD (lTextOutReturn);

            sprintf (szOutputString, "Page file in use:  \t%3.1d%%",
                dwPercentUsed);
            nStringLength = lstrlen (szOutputString) * sizeof (CHAR);
            lTextOutReturn = TabbedTextOut (hdc, nX, nY,
                szOutputString, nStringLength, 0, NULL, 0);
            nY += HIWORD (lTextOutReturn);

            EndPaint (hWnd, &ps);
        }
        break;

      case WM_DESTROY:
         FreeAllocatedMemory();
         // Tell WinHelp we don't need it any more...
               WinHelp (hWnd, APPNAME".HLP", HELP_QUIT,(DWORD)0);
         PostQuitMessage(0);
         break;

      default:
         return (DefWindowProc(hWnd, message, wParam, lParam));
   }
   return (0);
}

//
//  FUNCTION: About(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for "About" dialog box
//       This version allows greater flexibility over the contents of the 'About' box,
//       by pulling out values from the 'Version' resource.
//
//  MESSAGES:
//
// WM_INITDIALOG - initialize dialog box
// WM_COMMAND    - Input received
//
//
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
   static  HFONT hfontDlg;    // Font for dialog text
   static   HFONT hFinePrint; // Font for 'fine print' in dialog
   DWORD   dwVerInfoSize;     // Size of version information block
   LPSTR   lpVersion;         // String pointer to 'version' text
   DWORD   dwVerHnd=0;        // An 'ignored' parameter, always '0'
   UINT    uVersionLen;
   WORD    wRootLen;
   BOOL    bRetCode;
   int     i;
   char    szFullPath[256];
   char    szResult[256];
   char    szGetName[256];
   DWORD dwVersion;
   char  szVersion[40];
   DWORD dwResult;

   switch (message) {
        case WM_INITDIALOG:
         ShowWindow (hDlg, SW_HIDE);

         if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE)
         {
            hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, 0,
                                  VARIABLE_PITCH | FF_DONTCARE, "");
            hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, 0,
                                    VARIABLE_PITCH | FF_DONTCARE, "");
         }
         else
         {
            hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                  VARIABLE_PITCH | FF_SWISS, "");
            hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                    VARIABLE_PITCH | FF_SWISS, "");
         }

         CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
         GetModuleFileName (hInst, szFullPath, sizeof(szFullPath));

         // Now lets dive in and pull out the version information:
         dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd);
         if (dwVerInfoSize) {
            LPSTR   lpstrVffInfo;
            HANDLE  hMem;
            hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
            lpstrVffInfo  = GlobalLock(hMem);
            GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpstrVffInfo);
            // The below 'hex' value looks a little confusing, but
            // essentially what it is, is the hexidecimal representation
            // of a couple different values that represent the language
            // and character set that we are wanting string values for.
            // 040904E4 is a very common one, because it means:
            //   US English, Windows MultiLingual characterset
            // Or to pull it all apart:
            // 04------        = SUBLANG_ENGLISH_USA
            // --09----        = LANG_ENGLISH
            // --11----        = LANG_JAPANESE
            // ----04E4 = 1252 = Codepage for Windows:Multilingual

            lstrcpy(szGetName, GetStringRes(IDS_VER_INFO_LANG));

            wRootLen = lstrlen(szGetName); // Save this position

            // Set the title of the dialog:
            lstrcat (szGetName, "ProductName");
            bRetCode = VerQueryValue((LPVOID)lpstrVffInfo,
               (LPSTR)szGetName,
               (LPVOID)&lpVersion,
               (UINT *)&uVersionLen);

            // Notice order of version and string...
            if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE)
            {
               lstrcpy(szResult, lpVersion);
               lstrcat(szResult, "    o [ W        ");
            }
            else
            {
               lstrcpy(szResult, "About ");
               lstrcat(szResult, lpVersion);
            }

            // -----------------------------------------------------

            SetWindowText (hDlg, szResult);

            // Walk through the dialog items that we want to replace:
            for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) {
               GetDlgItemText(hDlg, i, szResult, sizeof(szResult));
               szGetName[wRootLen] = (char)0;
               lstrcat (szGetName, szResult);
               uVersionLen   = 0;
               lpVersion     = NULL;
               bRetCode      =  VerQueryValue((LPVOID)lpstrVffInfo,
                  (LPSTR)szGetName,
                  (LPVOID)&lpVersion,
                  (UINT *)&uVersionLen);

               if ( bRetCode && uVersionLen && lpVersion) {
               // Replace dialog item text with version info
                  lstrcpy(szResult, lpVersion);
                  SetDlgItemText(hDlg, i, szResult);
               }
               else
               {
                  dwResult = GetLastError();

                  wsprintf(szResult, GetStringRes(IDS_VERSION_ERROR), dwResult);
                  SetDlgItemText (hDlg, i, szResult);
               }
               SendMessage (GetDlgItem (hDlg, i), WM_SETFONT,
                  (UINT)((i==DLG_VERLAST)?hFinePrint:hfontDlg),
                  TRUE);
            } // for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++)

            GlobalUnlock(hMem);
            GlobalFree(hMem);

         } else {
            // No version information available.
         } // if (dwVerInfoSize)

            SendMessage (GetDlgItem (hDlg, IDC_LABEL), WM_SETFONT,
            (WPARAM)hfontDlg,(LPARAM)TRUE);

         // We are  using GetVersion rather then GetVersionEx
         // because earlier versions of Windows NT and Win32s
         // didn't include GetVersionEx:
         dwVersion = GetVersion();

         if (dwVersion < 0x80000000) {
            // Windows NT
            wsprintf (szVersion, "Microsoft Windows NT %u.%u (Build: %u)",
               (DWORD)(LOBYTE(LOWORD(dwVersion))),
               (DWORD)(HIBYTE(LOWORD(dwVersion))),
                    (DWORD)(HIWORD(dwVersion)) );
         } else if (LOBYTE(LOWORD(dwVersion))<4) {
            // Win32s
                wsprintf (szVersion, "Microsoft Win32s %u.%u (Build: %u)",
               (DWORD)(LOBYTE(LOWORD(dwVersion))),
               (DWORD)(HIBYTE(LOWORD(dwVersion))),
                    (DWORD)(HIWORD(dwVersion) & ~0x8000) );
         } else {
            // Windows 95
                wsprintf (szVersion, "Microsoft Windows 95 %u.%u",
                    (DWORD)(LOBYTE(LOWORD(dwVersion))),
                    (DWORD)(HIBYTE(LOWORD(dwVersion))) );
         }

          SetWindowText (GetDlgItem(hDlg, IDC_OSVERSION), szVersion);
         ShowWindow (hDlg, SW_SHOW);
         return (TRUE);

      case WM_COMMAND:
         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
            EndDialog(hDlg, TRUE);
            DeleteObject (hfontDlg);
            DeleteObject (hFinePrint);
            return (TRUE);
         }
         break;
   }

    return FALSE;
}

//
//   FUNCTION: CenterWindow(HWND, HWND)
//
//   PURPOSE: Centers one window over another.
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
//       This functionwill center one window over another ensuring that
//    the placement of the window is within the 'working area', meaning
//    that it is both within the display limits of the screen, and not
//    obscured by the tray or other framing elements of the desktop.
BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
{
   RECT    rChild, rParent, rWorkArea;
   int     wChild, hChild, wParent, hParent;
   int     xNew, yNew;
   BOOL  bResult;

   // Get the Height and Width of the child window
   GetWindowRect (hwndChild, &rChild);
   wChild = rChild.right - rChild.left;
   hChild = rChild.bottom - rChild.top;

   // Get the Height and Width of the parent window
   GetWindowRect (hwndParent, &rParent);
   wParent = rParent.right - rParent.left;
   hParent = rParent.bottom - rParent.top;

   // Get the limits of the 'workarea'
   bResult = SystemParametersInfo(
      SPI_GETWORKAREA,  // system parameter to query or set
      sizeof(RECT),
      &rWorkArea,
      0);
   if (!bResult) {
      rWorkArea.left = rWorkArea.top = 0;
      rWorkArea.right = GetSystemMetrics(SM_CXSCREEN);
      rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
   }

   // Calculate new X position, then adjust for workarea
   xNew = rParent.left + ((wParent - wChild) /2);
   if (xNew < rWorkArea.left) {
      xNew = rWorkArea.left;
   } else if ((xNew+wChild) > rWorkArea.right) {
      xNew = rWorkArea.right - wChild;
   }

   // Calculate new Y position, then adjust for workarea
   yNew = rParent.top  + ((hParent - hChild) /2);
   if (yNew < rWorkArea.top) {
      yNew = rWorkArea.top;
   } else if ((yNew+hChild) > rWorkArea.bottom) {
      yNew = rWorkArea.bottom - hChild;
   }

   // Set it, and return
   return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}

//---------------------------------------------------------------------------
//
// FUNCTION:    GetStringRes (int id INPUT ONLY)
//
// COMMENTS:    Load the resource string with the ID given, and return a
//              pointer to it.  Notice that the buffer is common memory so
//              the string must be used before this call is made a second time.
//
//---------------------------------------------------------------------------

LPTSTR   GetStringRes (int id)
{
  static TCHAR buffer[MAX_PATH];

  buffer[0]=0;
  LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
  return buffer;
}


LEAKYBIN.H

//---------------------------------------------------------------------------
//  LEAKYBIN.H
//---------------------------------------------------------------------------

#ifdef SHOW_MEMORY_USAGE
#include "..\appmem\pub\appmem.h"
#defineG_ALLOCGlobalAllocP
#define G_FREEGlobalFreeP
#define G_REALLOCGlobalReallocP

#else
#defineG_ALLOCGlobalAlloc
#define G_FREEGlobalFree
#define G_REALLOCGlobalRealloc

#endif

typedef struct _MEMORY_ALLOC_BLOCK {
struct _MEMORY_ALLOC_BLOCK*pNext;
} MEMORY_ALLOC_BLOCK, *PMEMORY_ALLOC_BLOCK;

#define ALLOCATION_SIZE(4096*10)
#define TIME_INTERVAL(100)
#define LEAK_TIMER13

#define IDM_EXIT           101
#define IDM_START          201
#define IDM_STOP           202
#define IDM_RESET          203

#define IDM_ABOUT          301
#define IDM_HELPTOPICS     302

#define IDC_STATIC -1

#define DLG_VERFIRST        400
#define IDC_COMPANY         DLG_VERFIRST
#define IDC_FILEDESC        DLG_VERFIRST+1
#define IDC_PRODVER         DLG_VERFIRST+2
#define IDC_COPYRIGHT       DLG_VERFIRST+3
#define IDC_OSVERSION       DLG_VERFIRST+4
#define IDC_TRADEMARK       DLG_VERFIRST+5
#define DLG_VERLAST         DLG_VERFIRST+5

#define IDC_LABEL           DLG_VERLAST+1

#define IDS_APP_TITLE       500
#define IDS_DISPLAYCHANGED  501
#define IDS_VER_INFO_LANG   502
#define IDS_VERSION_ERROR   503
#define IDS_NO_HELP         504


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Generic.RC
//

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

Using the Signal Generator Performance DLL

Overview:

The Signal Generator performance DLL provides 3 waveforms at 5
different periods for use with the Performance Monitor to provide
data at a known rate and value. This can be useful in testing
applications that use performance data and need predictable values to
test against.

Building and Installing:

The DLL must be built from the source code using the Windows NT build
Utilities and then installed on the target system using the following
steps:

    1. Copy the PerfGen.DLL that was built to the %systemroot%\system32
    directory.

    2. load the driver entries into the registry using the following
    command line:

        REGEDIT PERFGEN.REG

    3. load the performance names into the registry using the command
    line:

        LODCTR PERFGEN.INI

At this point all the software is installed and it ready to use.
Start Perfmon and select the "Signal Generator" object to select the
desired wave form and period.

NOTE: The system may need to be restarted after these instructions
are completed for this object to be seen by remote computers.


DATAGEN.H

/*++

Copyright (c) 1995-6 Microsoft Corporation

Module Name:

      DATAGEN.h

Abstract:

    Header file for the signal generator performance counters.

    This file contains definitions to construct the dynamic data
    which is returned by the Configuration Registry.  Data from
    various system API calls is placed into the structures shown
    here.

Author:

    Bob Watson  28-Jul-1995

Revision History:

--*/

#ifndef _DATAGEN_H_
#define _DATAGEN_H_

//
//  insure packing is done to the 8 byte align longlong data values. This
//  will eliminate alignment faults on RISC platforms. The fields can be
//  manually arranged to minimize or eliminate wasted space if necessary.
//
#pragma pack (8)

//
//  Extensible Object definitions
//

//  Update the following sort of define when adding an object type.

#define SIGGEN_NUM_PERF_OBJECT_TYPES 1

//----------------------------------------------------------------------------

//
//  Perf Gen Resource object type counter definitions.
//
//  This is the counter structure presently returned by the generator
//

typedef struct _SIGGEN_DATA_DEFINITION {
    PERF_OBJECT_TYPESigGenObjectType;
    PERF_COUNTER_DEFINITIONSineWaveDef;
    PERF_COUNTER_DEFINITIONTriangleWaveDef;
    PERF_COUNTER_DEFINITIONSquareWaveDef;
    PERF_COUNTER_DEFINITION ConstantValueDef;
} SIGGEN_DATA_DEFINITION;

//
// This is the block of data that corresponds to each instance of the
// object. This structure will immediately follow the instance definition
// data structure
//

typedef struct _SIGGEN_COUNTER {
    PERF_COUNTER_BLOCK      CounterBlock;
    DWORD                   dwSineWaveValue;
    DWORD                   dwTriangleWaveValue;
    DWORD                   dwSquareWaveValue;
    DWORD                   dwConstantValue;
} SIGGEN_COUNTER, *PSIGGEN_COUNTER;

#pragma pack ()

#endif //_DATAGEN_H_


GENCTRNM.H

//
//  genctrnm.h
//
//  Offset definition file for exensible counter objects and counters
//
//  These "relative" offsets must start at 0 and be multiples of 2 (i.e.
//  even numbers). In the Open Procedure, they will be added to the
//  "First Counter" and "First Help" values fo the device they belong to,
//  in order to determine the  absolute location of the counter and
//  object names and corresponding help text in the registry.
//
//  this file is used by the extensible counter DLL code as well as the
//  counter name and help text definition file (.INI) file that is used
//  by LODCTR to load the names into the registry.
//
#define SIGGEN_OBJ      0
#define SINE_WAVE       2
#define TRIANGLE_WAVE   4
#define SQUARE_WAVE     6
#define CONSTANT_VALUE  8


PERFGEN.C

/*++

Copyright (c) 1995-6  Microsoft Corporation

Module Name:

    perfgen.c

Abstract:

    This file implements an Extensible Performance Object that displays
    generated signals

Created:

    Bob Watson  28-Jul-1995

Revision History

--*/

//
//  Include Files
//

#include <windows.h>
#include <string.h>
#include <winperf.h>
#include <math.h>
#include "genctrs.h" // error message definition
#include "perfmsg.h"
#include "perfutil.h"
#include "datagen.h"

//  define constant value counter's value here, any number will do.

#define CONSTANT_VALUE_VALUE    49
//
//  References to constants which initialize the Object type definitions
//

extern SIGGEN_DATA_DEFINITION SigGenDataDefinition;

DWORD   dwOpenCount = 0;        // count of "Open" threads
BOOL    bInitOK = FALSE;        // true = DLL initialized OK

//
//  Function Prototypes
//
//      these are used to insure that the data collection functions
//      accessed by Perflib will have the correct calling format.
//

PM_OPEN_PROC    OpenSigGenPerformanceData;
PM_COLLECT_PROC CollectSigGenPerformanceData;
PM_CLOSE_PROC   CloseSigGenPerformanceData;

typedef struct _WAVE_DATA {
    DWORD   dwPeriod;
    DWORD   dwAmplitude;
    LPWSTR  szInstanceName;
} WAVE_DATA, *PWAVE_DATA;

static WAVE_DATA wdInstance[]  =
{
    {1000,      100, L"  1 Second"},
    {10000,     100, L" 10 Second"},
    {20000,     100, L" 20 Second"},
    {50000,     100, L" 50 Second"},
    {100000,    100, L"100 Second"}
};

static const DWORD    NUM_INSTANCES =
    (sizeof(wdInstance)/sizeof(wdInstance[0]));

static double   dPi = 3.1415926590;
static double   d2Pi = 2.0 * 3.1415926590;

static
DWORD
GetTimeInMilliSeconds ()
{
    SYSTEMTIME  st;
    DWORD       dwReturn;

    GetSystemTime (&st);
    dwReturn = (DWORD)st.wMilliseconds;
    dwReturn += (DWORD)st.wSecond * 1000L;
    dwReturn += (DWORD)st.wMinute * 60000L;
    dwReturn += (DWORD)st.wHour * 3600000L;
    dwReturn += (DWORD)st.wDay * 86500000L;

    // that's good enough for what it's for

    return dwReturn;
}

DWORD APIENTRY
OpenSigGenPerformanceData(
    LPWSTR lpDeviceNames
    )

/*++

Routine Description:

    This routine will initialize the data structures used to pass
    data back to the registry

Arguments:

    Pointer to object ID of each device to be opened (PerfGen)

Return Value:

    None.

--*/

{
    LONG status;
    HKEY hKeyDriverPerf;
    DWORD size;
    DWORD type;
    DWORD dwFirstCounter;
    DWORD dwFirstHelp;

    //
    //  Since WINLOGON is multi-threaded and will call this routine in
    //  order to service remote performance queries, this library
    //  must keep track of how many times it has been opened (i.e.
    //  how many threads have accessed it). the registry routines will
    //  limit access to the initialization routine to only one thread
    //  at a time so synchronization (i.e. reentrancy) should not be
    //  a problem
    //

    if (!dwOpenCount) {
        // open Eventlog interface

        hEventLog = MonOpenEventLog();

        // get counter and help index base values from registry
        //      Open key to registry entry
        //      read First Counter and First Help values
        //      update static data strucutures by adding base to
        //          offset value in structure.

        status = RegOpenKeyEx (
            HKEY_LOCAL_MACHINE,
        "SYSTEM\\CurrentControlSet\\Services\\PerfGen\\Performance",
            0L,
        KEY_READ,
            &hKeyDriverPerf);

        if (status != ERROR_SUCCESS) {
            REPORT_ERROR_DATA (GENPERF_UNABLE_OPEN_DRIVER_KEY, LOG_USER,
                &status, sizeof(status));
            // this is fatal, if we can't get the base values of the
            // counter or help names, then the names won't be available
            // to the requesting application  so there's not much
            // point in continuing.
            goto OpenExitPoint;
        }

        size = sizeof (DWORD);
        status = RegQueryValueEx(
                    hKeyDriverPerf,
            "First Counter",
                    0L,
                    &type,
                    (LPBYTE)&dwFirstCounter,
                    &size);

        if (status != ERROR_SUCCESS) {
            REPORT_ERROR_DATA (GENPERF_UNABLE_READ_FIRST_COUNTER, LOG_USER,
                &status, sizeof(status));
            // this is fatal, if we can't get the base values of the
            // counter or help names, then the names won't be available
            // to the requesting application  so there's not much
            // point in continuing.
            goto OpenExitPoint;
        }

        size = sizeof (DWORD);
        status = RegQueryValueEx(
                    hKeyDriverPerf,
            "First Help",
                    0L,
                    &type,
                    (LPBYTE)&dwFirstHelp,
    &size);

        if (status != ERROR_SUCCESS) {
            REPORT_ERROR_DATA (GENPERF_UNABLE_READ_FIRST_HELP, LOG_USER,
                &status, sizeof(status));
            // this is fatal, if we can't get the base values of the
            // counter or help names, then the names won't be available
            // to the requesting application  so there's not much
            // point in continuing.
            goto OpenExitPoint;
        }

        //
        //  NOTE: the initialization program could also retrieve
        //      LastCounter and LastHelp if they wanted to do
        //      bounds checking on the new number. e.g.
        //
        //      counter->CounterNameTitleIndex += dwFirstCounter;
        //      if (counter->CounterNameTitleIndex > dwLastCounter) {
        //          LogErrorToEventLog (INDEX_OUT_OF_BOUNDS);
        //      }

        SigGenDataDefinition.SigGenObjectType.ObjectNameTitleIndex += dwFirstCounter;
        SigGenDataDefinition.SigGenObjectType.ObjectHelpTitleIndex += dwFirstHelp;

        // assign index of default counter (Sine Wave)
        SigGenDataDefinition.SigGenObjectType.DefaultCounter = 0;

        SigGenDataDefinition.SineWaveDef.CounterNameTitleIndex += dwFirstCounter;
        SigGenDataDefinition.SineWaveDef.CounterHelpTitleIndex += dwFirstHelp;

        SigGenDataDefinition.TriangleWaveDef.CounterNameTitleIndex += dwFirstCounter;
        SigGenDataDefinition.TriangleWaveDef.CounterHelpTitleIndex += dwFirstHelp;

        SigGenDataDefinition.SquareWaveDef.CounterNameTitleIndex += dwFirstCounter;
        SigGenDataDefinition.SquareWaveDef.CounterHelpTitleIndex += dwFirstHelp;

        SigGenDataDefinition.ConstantValueDef.CounterNameTitleIndex += dwFirstCounter;
        SigGenDataDefinition.ConstantValueDef.CounterHelpTitleIndex += dwFirstHelp;

        RegCloseKey (hKeyDriverPerf); // close key to registry

        bInitOK = TRUE; // ok to use this function
    }

    dwOpenCount++;  // increment OPEN counter

    status = ERROR_SUCCESS; // for successful exit

OpenExitPoint:

    return status;
}

DWORD APIENTRY
CollectSigGenPerformanceData(
    IN      LPWSTR  lpValueName,
    IN OUT  LPVOID  *lppData,
    IN OUT  LPDWORD lpcbTotalBytes,
    IN OUT  LPDWORD lpNumObjectTypes
)
/*++

Routine Description:

    This routine will return the data for the Signal Generator counters.

Arguments:

   IN       LPWSTR   lpValueName
         pointer to a wide character string passed by registry.

   IN OUT   LPVOID   *lppData
         IN: pointer to the address of the buffer to receive the completed
            PerfDataBlock and subordinate structures. This routine will
            append its data to the buffer starting at the point referenced
            by *lppData.
         OUT: points to the first byte after the data structure added by this
            routine. This routine updated the value at lppdata after appending
            its data.

   IN OUT   LPDWORD  lpcbTotalBytes
         IN: the address of the DWORD that tells the size in bytes of the
            buffer referenced by the lppData argument
         OUT: the number of bytes added by this routine is writted to the
            DWORD pointed to by this argument

   IN OUT   LPDWORD  NumObjectTypes
         IN: the address of the DWORD to receive the number of objects added
            by this routine
         OUT: the number of objects added by this routine is writted to the
            DWORD pointed to by this argument

Return Value:

      ERROR_MORE_DATA if buffer passed is too small to hold data
         any error conditions encountered are reported to the event log if
         event logging is enabled.

      ERROR_SUCCESS  if success or any other error. Errors, however are
         also reported to the event log.

--*/
{
    //  Variables for reformating the data

    PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
    DWORD   dwThisInstance;
    ULONG SpaceNeeded;
    SIGGEN_DATA_DEFINITION *pSigGenDataDefinition;
    DWORD   dwQueryType;
    DWORD   dwTime;
    DWORD   dwPhase;
    double  dPhase, dSin;
    LONG    lValue;
    PSIGGEN_COUNTER   pSC;

    //
    // before doing anything else, see if Open went OK
    //
    if (!bInitOK) {
        // unable to continue because open failed.
    *lpcbTotalBytes = (DWORD) 0;
    *lpNumObjectTypes = (DWORD) 0;
        return ERROR_SUCCESS; // yes, this is a successful exit
    }

    // see if this is a foreign (i.e. non-NT) computer data request
    //
    dwQueryType = GetQueryType (lpValueName);

    if (dwQueryType == QUERY_FOREIGN) {
        // this routine does not service requests for data from
        // Non-NT computers
    *lpcbTotalBytes = (DWORD) 0;
    *lpNumObjectTypes = (DWORD) 0;
        return ERROR_SUCCESS;
    }

    if (dwQueryType == QUERY_ITEMS){
if ( !(IsNumberInUnicodeList (SigGenDataDefinition.SigGenObjectType.ObjectNameTitleIndex, lpValueName))) {
            // request received for data object not provided by this routine
            *lpcbTotalBytes = (DWORD) 0;
        *lpNumObjectTypes = (DWORD) 0;
            return ERROR_SUCCESS;
        }
    }

    pSigGenDataDefinition = (SIGGEN_DATA_DEFINITION *) *lppData;

    SpaceNeeded = sizeof(SIGGEN_DATA_DEFINITION) +
          (NUM_INSTANCES * (sizeof(PERF_INSTANCE_DEFINITION) +
          (24) +    // size of instance names
          sizeof (SIGGEN_COUNTER)));

    if ( *lpcbTotalBytes < SpaceNeeded ) {
    *lpcbTotalBytes = (DWORD) 0;
    *lpNumObjectTypes = (DWORD) 0;
        return ERROR_MORE_DATA;
    }

    // Get current time for this sample
    //
    dwTime = GetTimeInMilliSeconds();
    //
    // Copy the (constant, initialized) Object Type and counter definitions
    //  to the caller's data buffer
    //
    memmove(pSigGenDataDefinition,
   &SigGenDataDefinition,
   sizeof(SIGGEN_DATA_DEFINITION));
    //
    //Create data for return for each instance
    //
    pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
                                 &pSigGenDataDefinition[1];

    for (dwThisInstance = 0; dwThisInstance < NUM_INSTANCES; dwThisInstance++) {

        MonBuildInstanceDefinition(
            pPerfInstanceDefinition,
            (PVOID *)&pSC,
            0,
            0,
            (DWORD)-1, // use name
            wdInstance[dwThisInstance].szInstanceName);

        pSC->CounterBlock.ByteLength = sizeof (SIGGEN_COUNTER);

        //**********************************************************
        //
        // for this particular example, the data is "created" here.
        // normally it would be read from the appropriate device or
        // application program.
        //
        //**********************************************************

        // comput phase for this instance period
        dwPhase = dwTime % wdInstance[dwThisInstance].dwPeriod;
        //
        // compute sinewave value here
        //
        dPhase = (double)dwPhase / (double)wdInstance[dwThisInstance].dwPeriod;
        dPhase *= d2Pi;

        // the cosine function is used to keep the phase aligned with the
        // other wave forms
        dSin = -cos(dPhase);
        // adjust amplitude and add .5 to round to integer correctly
        dSin *= (double)((wdInstance[dwThisInstance].dwAmplitude) / 2.0) + 0.5 ;

        lValue = (LONG)dSin;
        lValue += wdInstance[dwThisInstance].dwAmplitude / 2;   // to move negative values above 0

        // save sine value
        pSC->dwSineWaveValue = (DWORD)lValue;

        // compute triangle wave value here

        if (dwPhase < (wdInstance[dwThisInstance].dwPeriod / 2)) {
            lValue = (LONG)((dwPhase * wdInstance[dwThisInstance].dwAmplitude) / (wdInstance[dwThisInstance].dwPeriod / 2));
        } else {
            lValue = (LONG)(((wdInstance[dwThisInstance].dwPeriod - dwPhase) * wdInstance[dwThisInstance].dwAmplitude) /
                    (wdInstance[dwThisInstance].dwPeriod / 2));
        }
        // save triangle value
        pSC->dwTriangleWaveValue = (DWORD) lValue;

        //
        //  compute square wave value
        //
        if (dwPhase <= (wdInstance[dwThisInstance].dwPeriod / 2)) {
            lValue = 0;
        } else {
            lValue = (LONG)wdInstance[dwThisInstance].dwAmplitude;
        }
        // save square value
        pSC->dwSquareWaveValue = (DWORD) lValue;

        // finally the constant value (same fore every instance)
        pSC->dwConstantValue = (DWORD)CONSTANT_VALUE_VALUE;

        // update instance pointer for next instance
        pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pSC[1];
    }
    // update arguments for return

    *lppData = (PVOID)pPerfInstanceDefinition;

    *lpNumObjectTypes = 1;

    pSigGenDataDefinition->SigGenObjectType.TotalByteLength =
        *lpcbTotalBytes = (PBYTE)pPerfInstanceDefinition -
                          (PBYTE) pSigGenDataDefinition;

    // update instance count
    pSigGenDataDefinition->SigGenObjectType.NumInstances = NUM_INSTANCES;

    return ERROR_SUCCESS;
}

DWORD APIENTRY
CloseSigGenPerformanceData(
)

/*++

Routine Description:

    This routine closes the open handles to the Signal Gen counters.

Arguments:

    None.

Return Value:

    ERROR_SUCCESS

--*/

{
    if (!(--dwOpenCount)) { // when this is the last thread...

        MonCloseEventLog();
    }

    return ERROR_SUCCESS;

}


PERFMSG.H

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    perfmsg.h

Abstract:

    This file provides the macros and definitions used by the extensible
    counters for reporting events to the event logging facility

Author:

    Bob Watson  28-Jul-1995

Revision History:

--*/
#ifndef  _PERFMSG_H_
#define  _PERFMSG_H_
//
// Report error message ID's for Counters
//

#define APP_NAME  "perfgen"

//
// The constant below defines how many (if any) messages will be reported
// to the event logger. As the number goes up in value more and more events
// will be reported. The purpose of this is to allow lots of messages during
// development and debugging (e.g. a message level of 3) to a minimum of
// messages (e.g. operational messages with a level of 1) or no messages if
// message logging inflicts too much of a performance penalty. Right now
// this is a compile time constant, but could later become a registry entry.
//
//    Levels:  LOG_NONE = No event log messages ever
//             LOG_USER = User event log messages (e.g. errors)
//             LOG_DEBUG = Minimum Debugging
//             LOG_VERBOSE = Maximum Debugging
//

#define  LOG_NONE     0
#define  LOG_USER     1
#define  LOG_DEBUG    2
#define  LOG_VERBOSE  3

#define  MESSAGE_LEVEL_DEFAULT  LOG_USER

// define macros
//
// Format for event log calls without corresponding insertion strings is:
//    REPORT_xxx (message_value, message_level)
//       where:
//          xxx is the severity to be displayed in the event log
//          message_value is the numeric ID from above
//          message_level is the "filtering" level of error reporting
//             using the error levels above.
//
// if the message has a corresponding insertion string whose symbol conforms
// to the format CONSTANT = numeric value and CONSTANT_S = string constant for
// that message, then the
//
//    REPORT_xxx_STRING (message_value, message_level)
//
// macro may be used.
//

//
// REPORT_SUCCESS was intended to show Success in the error log, rather it
// shows "N/A" so for now it's the same as information, though it could
// (should) be changed  in the future
//

#define REPORT_SUCCESS(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \
   0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)

#define REPORT_INFORMATION(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \
   0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)

#define REPORT_WARNING(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, \
   0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)

#define REPORT_ERROR(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, \
   0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)

#define REPORT_INFORMATION_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \
   0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE)

#define REPORT_WARNING_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, \
   0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE)

#define REPORT_ERROR_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, \
   0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE)

// External Variables

extern HANDLE hEventLog;   // handle to event log
extern DWORD  dwLogUsers;  // counter of event log using routines
extern DWORD  MESSAGE_LEVEL; // event logging detail level

#endif //_PERFMSG_H_


PERFUTIL.C

/*++

Copyright (c) 1995-6  Microsoft Corporation

Module Name:

    perfutil.c

Abstract:

    This file implements the utility routines used to construct the
    common parts of a PERF_INSTANCE_DEFINITION (see winperf.h) and
    perform event logging functions.

Created:

    Bob Watson  28-Jul-1995

Revision History:

--*/
//
//  include files
//
#include <windows.h>
#include <string.h>
#include <winperf.h>
#include "genctrs.h"     // error message definition
#include "perfmsg.h"
#include "perfutil.h"

//
// Global data definitions.
//

ULONG ulInfoBufferSize = 0;

HANDLE hEventLog = NULL;      // event log handle for reporting events
                              // initialized in Open... routines
DWORD  dwLogUsers = 0;        // count of functions using event log

DWORD MESSAGE_LEVEL = 0;

WCHAR GLOBAL_STRING[] = L"Global";
WCHAR FOREIGN_STRING[] = L"Foreign";
WCHAR COSTLY_STRING[] = L"Costly";

WCHAR NULL_STRING[] = L"\0";    // pointer to null string

// test for delimiter, end of line and non-digit characters
// used by IsNumberInUnicodeList routine
//
#define DIGIT       1
#define DELIMITER   2
#define INVALID     3

#define EvalThisChar(c,d) ( \
     (c == d) ? DELIMITER : \
     (c == 0) ? DELIMITER : \
     (c < (WCHAR)'0') ? INVALID : \
     (c > (WCHAR)'9') ? INVALID : \
     DIGIT)

HANDLE
MonOpenEventLog (
)
/*++

Routine Description:

    Reads the level of event logging from the registry and opens the
        channel to the event logger for subsequent event log entries.

Arguments:

      None

Return Value:

    Handle to the event log for reporting events.
    NULL if open not successful.

--*/
{
    HKEY hAppKey;
    TCHAR LogLevelKeyName[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib";
    TCHAR LogLevelValueName[] = "EventLogLevel";

    LONG lStatus;

    DWORD dwLogLevel;
    DWORD dwValueType;
    DWORD dwValueSize;

    // if global value of the logging level not initialized or is disabled,
    //  check the registry to see if it should be updated.

    if (!MESSAGE_LEVEL) {

       lStatus = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                               LogLevelKeyName,
                               0,
                               KEY_READ,
                               &hAppKey);

       dwValueSize = sizeof (dwLogLevel);

       if (lStatus == ERROR_SUCCESS) {
            lStatus = RegQueryValueEx (hAppKey,
                               LogLevelValueName,
                               (LPDWORD)NULL,
                               &dwValueType,
                               (LPBYTE)&dwLogLevel,
                               &dwValueSize);

            if (lStatus == ERROR_SUCCESS) {
               MESSAGE_LEVEL = dwLogLevel;
            } else {
               MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
            }
            RegCloseKey (hAppKey);
       } else {
         MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
       }
    }

    if (hEventLog == NULL){
         hEventLog = RegisterEventSource (
            (LPTSTR)NULL,            // Use Local Machine
            APP_NAME);               // event log app name to find in registry

         if (hEventLog != NULL) {
            REPORT_INFORMATION (UTIL_LOG_OPEN, LOG_DEBUG);
         }
    }

    if (hEventLog != NULL) {
         dwLogUsers++;           // increment count of perfctr log users
    }
    return (hEventLog);
}

VOID
MonCloseEventLog (
)
/*++

Routine Description:

      Closes the handle to the event logger if this is the last caller

Arguments:

      None

Return Value:

      None

--*/
{
    if (hEventLog != NULL) {
        dwLogUsers--;         // decrement usage
        if (dwLogUsers <= 0) {    // and if we're the last, then close up log
            REPORT_INFORMATION (UTIL_CLOSING_LOG, LOG_DEBUG);
            DeregisterEventSource (hEventLog);
        }
    }
}

DWORD
GetQueryType (
    IN LPWSTR lpValue
)
/*++

GetQueryType

    returns the type of query described in the lpValue string so that
    the appropriate processing method may be used

Arguments

    IN lpValue
        string passed to PerfRegQuery Value for processing

Return Value

    QUERY_GLOBAL
        if lpValue == 0 (null pointer)
           lpValue == pointer to Null string
           lpValue == pointer to "Global" string

    QUERY_FOREIGN
        if lpValue == pointer to "Foriegn" string

    QUERY_COSTLY
        if lpValue == pointer to "Costly" string

    otherwise:

    QUERY_ITEMS

--*/
{
    WCHAR   *pwcArgChar, *pwcTypeChar;
    BOOL    bFound;

    if (lpValue == 0) {
        return QUERY_GLOBAL;
    } else if (*lpValue == 0) {
        return QUERY_GLOBAL;
    }

    // check for "Global" request

    pwcArgChar = lpValue;
    pwcTypeChar = GLOBAL_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string

    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_GLOBAL;

    // check for "Foreign" request

    pwcArgChar = lpValue;
    pwcTypeChar = FOREIGN_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string

    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_FOREIGN;

    // check for "Costly" request

    pwcArgChar = lpValue;
    pwcTypeChar = COSTLY_STRING;
    bFound = TRUE;  // assume found until contradicted

    // check to the length of the shortest string

    while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
        if (*pwcArgChar++ != *pwcTypeChar++) {
            bFound = FALSE; // no match
            break;          // bail out now
        }
    }

    if (bFound) return QUERY_COSTLY;

    // if not Global and not Foreign and not Costly,
    // then it must be an item list

    return QUERY_ITEMS;

}

BOOL
IsNumberInUnicodeList (
    IN DWORD   dwNumber,
    IN LPWSTR  lpwszUnicodeList
)
/*++

IsNumberInUnicodeList

Arguments:

    IN dwNumber
        DWORD number to find in list

    IN lpwszUnicodeList
        Null terminated, Space delimited list of decimal numbers

Return Value:

    TRUE:
            dwNumber was found in the list of unicode number strings

    FALSE:
            dwNumber was not found in the list.

--*/
{
    DWORD   dwThisNumber;
    WCHAR   *pwcThisChar;
    BOOL    bValidNumber;
    BOOL    bNewItem;
    WCHAR   wcDelimiter;    // could be an argument to be more flexible

    if (lpwszUnicodeList == 0) return FALSE;    // null pointer, # not founde

    pwcThisChar = lpwszUnicodeList;
    dwThisNumber = 0;
    wcDelimiter = (WCHAR)' ';
    bValidNumber = FALSE;
    bNewItem = TRUE;

    while (TRUE) {
        switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
            case DIGIT:
                // if this is the first digit after a delimiter, then
                // set flags to start computing the new number
                if (bNewItem) {
                    bNewItem = FALSE;
                    bValidNumber = TRUE;
                }
                if (bValidNumber) {
                    dwThisNumber *= 10;
                    dwThisNumber += (*pwcThisChar - (WCHAR)'0');
                }
                break;

            case DELIMITER:
                // a delimter is either the delimiter character or the
                // end of the string ('\0') if when the delimiter has been
                // reached a valid number was found, then compare it to the
                // number from the argument list. if this is the end of the
                // string and no match was found, then return.
                //
                if (bValidNumber) {
                    if (dwThisNumber == dwNumber) return TRUE;
                    bValidNumber = FALSE;
                }
                if (*pwcThisChar == 0) {
                    return FALSE;
                } else {
                    bNewItem = TRUE;
                    dwThisNumber = 0;
                }
                break;

            case INVALID:
                // if an invalid character was encountered, ignore all
                // characters up to the next delimiter and then start fresh.
                // the invalid number is not compared.
                bValidNumber = FALSE;
                break;

            default:
                break;

        }
        pwcThisChar++;
    }
    return FALSE;
}   // IsNumberInUnicodeList

BOOL
MonBuildInstanceDefinition(
    PERF_INSTANCE_DEFINITION *pBuffer,
    PVOID *pBufferNext,
    DWORD ParentObjectTitleIndex,
    DWORD ParentObjectInstance,
    DWORD UniqueID,
    LPWSTR Name
    )
/*++

    MonBuildInstanceDefinition  -   Build an instance of an object

        Inputs:

            pBuffer         -   pointer to buffer where instance is to
                                be constructed

            pBufferNext     -   pointer to a pointer which will contain
                                next available location, DWORD aligned

            ParentObjectTitleIndex
                            -   Title Index of parent object type; 0 if
                                no parent object

            ParentObjectInstance
                            -   Index into instances of parent object
                                type, starting at 0, for this instances
                                parent object instance

            UniqueID        -   a unique identifier which should be used
                                instead of the Name for identifying
                                this instance

            Name            -   Name of this instance
--*/
{
    DWORD NameLength;
    LPWSTR pName;
    //
    //  Include trailing null in name size
    //

    NameLength = (lstrlenW(Name) + 1) * sizeof(WCHAR);

    pBuffer->ByteLength = sizeof(PERF_INSTANCE_DEFINITION) +
                          DWORD_MULTIPLE(NameLength);

    pBuffer->ParentObjectTitleIndex = ParentObjectTitleIndex;
    pBuffer->ParentObjectInstance = ParentObjectInstance;
    pBuffer->UniqueID = UniqueID;
    pBuffer->NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
    pBuffer->NameLength = NameLength;

    // copy name to name buffer
    pName = (LPWSTR)&pBuffer[1];
    RtlMoveMemory(pName,Name,NameLength);

    // update "next byte" pointer
    *pBufferNext = (PVOID) ((PCHAR) pBuffer + pBuffer->ByteLength);

    return 0;
}


PERFUTIL.H

/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright 1995 - 1998 Microsoft Corporation

Module Name:

    perfutil.h

Abstract:

    This file supports routines used to parse and
    create Performance Monitor Data Structures.
    It actually supports Performance Object types with
    multiple instances

Author:

    Bob Watson  28-Jul-1995

Revision History:

--*/
#ifndef _PERFUTIL_H_
#define _PERFUTIL_H_

// enable this define to log process heap data to the event log
#ifdef PROBE_HEAP_USAGE
#undef PROBE_HEAP_USAGE
#endif
//
//  Utility macro.  This is used to reserve a DWORD multiple of
//  bytes for Unicode strings embedded in the definitional data,
//  viz., object instance names.
//
#define DWORD_MULTIPLE(x) (((x+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD))

//    (assumes dword is 4 bytes long and pointer is a dword in size)
#define ALIGN_ON_DWORD(x) ((VOID *)( ((DWORD) x & 0x00000003) ? ( ((DWORD) x & 0xFFFFFFFC) + 4 ) : ( (DWORD) x ) ))

extern WCHAR  GLOBAL_STRING[];      // Global command (get all local ctrs)
extern WCHAR  FOREIGN_STRING[];           // get data from foreign computers
extern WCHAR  COSTLY_STRING[];
extern WCHAR  NULL_STRING[];

#define QUERY_GLOBAL    1
#define QUERY_ITEMS     2
#define QUERY_FOREIGN   3
#define QUERY_COSTLY    4

//
// The definition of the only routine of perfutil.c, It builds part of a
// performance data instance (PERF_INSTANCE_DEFINITION) as described in
// winperf.h
//

HANDLE MonOpenEventLog ();
VOID MonCloseEventLog ();
DWORD GetQueryType (IN LPWSTR);
BOOL IsNumberInUnicodeList (DWORD, LPWSTR);

BOOL
MonBuildInstanceDefinition(
    PERF_INSTANCE_DEFINITION *pBuffer,
    PVOID *pBufferNext,
    DWORD ParentObjectTitleIndex,
    DWORD ParentObjectInstance,
    DWORD UniqueID,
    LPWSTR Name
    );

typedef struct _LOCAL_HEAP_INFO_BLOCK {
    DWORD   AllocatedEntries;
    DWORD   AllocatedBytes;
    DWORD   FreeEntries;
    DWORD   FreeBytes;
} LOCAL_HEAP_INFO, *PLOCAL_HEAP_INFO;

//
//  Memory Probe macro
//
#ifdef PROBE_HEAP_USAGE

#define HEAP_PROBE()    { \
    DWORD   dwHeapStatus[5]; \
    NTSTATUS CallStatus; \
    dwHeapStatus[4] = __LINE__; \
    if (!(CallStatus = memprobe (dwHeapStatus, 16L, NULL))) { \
        REPORT_INFORMATION_DATA (TCP_HEAP_STATUS, LOG_DEBUG,    \
            &dwHeapStatus, sizeof(dwHeapStatus));  \
    } else {  \
        REPORT_ERROR_DATA (TCP_HEAP_STATUS_ERROR, LOG_DEBUG, \
            &CallStatus, sizeof (DWORD)); \
    } \
}

#else

#define HEAP_PROBE()    ;

#endif

#endif  //_PERFUTIL_H_


DATAGEN.C

/*++

Copyright (c) 1995-6  Microsoft Corporation

Module Name:

    datagen.c

Abstract:

    a file containing the constant data structures used by the Performance
    Monitor data for the Signal Generator Perf DLL

    This file contains a set of constant data structures which are
    currently defined for the Signal Generator Perf DLL.

Created:

    Bob Watson  28-Jul-1995

Revision History:

    None.

--*/
//
//  Include Files
//

#include <windows.h>
#include <winperf.h>
#include "genctrnm.h"
#include "datagen.h"

// dummy variable for field sizing.
static SIGGEN_COUNTER   sc;

//
//  Constant structure initializations
//      defined in datagen.h
//

SIGGEN_DATA_DEFINITION SigGenDataDefinition = {

    {sizeof(SIGGEN_DATA_DEFINITION) + sizeof(SIGGEN_COUNTER),
    sizeof(SIGGEN_DATA_DEFINITION),
    sizeof(PERF_OBJECT_TYPE),
    SIGGEN_OBJ,
    0,
    SIGGEN_OBJ,
    0,
    PERF_DETAIL_NOVICE,
    (sizeof(SIGGEN_DATA_DEFINITION)-sizeof(PERF_OBJECT_TYPE))/
        sizeof(PERF_COUNTER_DEFINITION),
    0   // assigned in Open Procedure
    PERF_NO_INSTANCES,
    0
    },
    {   sizeof(PERF_COUNTER_DEFINITION),
    SINE_WAVE,
    0,
    SINE_WAVE,
    0,
    0,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_RAWCOUNT,
    sizeof(sc.dwSineWaveValue),
    (DWORD)&(((PSIGGEN_COUNTER)0)->dwSineWaveValue)
    },
    {   sizeof(PERF_COUNTER_DEFINITION),
    TRIANGLE_WAVE,
    0,
    TRIANGLE_WAVE,
    0,
    0,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_RAWCOUNT,
    sizeof(sc.dwTriangleWaveValue),
    (DWORD)&(((PSIGGEN_COUNTER)0)->dwTriangleWaveValue)
    },
    {   sizeof(PERF_COUNTER_DEFINITION),
    SQUARE_WAVE,
    0,
    SQUARE_WAVE,
    0,
    0,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_RAWCOUNT,
    sizeof(sc.dwSquareWaveValue),
    (DWORD)&(((PSIGGEN_COUNTER)0)->dwSquareWaveValue)
    },
    {   sizeof(PERF_COUNTER_DEFINITION),
    CONSTANT_VALUE,
    0,
    CONSTANT_VALUE,
    0,
    0,
    PERF_DETAIL_NOVICE,
    PERF_COUNTER_RAWCOUNT,
    sizeof(sc.dwConstantValue),
    (DWORD)&(((PSIGGEN_COUNTER)0)->dwConstantValue)
    }
};


README.TXT

POP3 Sample

The POP3 sample includes a server that sets up a listening socket on the
POP3 port, then loops, accepting incoming connections from clients. Whenever
the server gets a connection, it sends a greeting message and starts a read
operation on the newly connected socket.


DEBUG.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       debug.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#ifndef __DEBUG_H__
#define __DEBUG_H__

#if DBG

extern  DWORD   PopInfoLevel;

#define DebugLog(x) LogEvent x

void    LogEvent(long, const char *, ...);
void    InitDebugSupport(void);

#define DEB_ERROR           0x00000001
#define DEB_WARN            0x00000002
#define DEB_TRACE           0x00000004
#define DEB_TRACE_PROT      0x00000008

#else

#define DebugLog(x)
#define InitDebugSupport()

#endif

#endif // __DEBUG_H__


EVENTS.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       events.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

HANDLE  hEventLog = INVALID_HANDLE_VALUE;
DWORD   LoggingLevel = 1;
WCHAR   EventSourceName[] = TEXT("Pop3 Server");

#define MAX_EVENT_STRINGS 8

//+---------------------------------------------------------------------------
//
//  Function:   InitializeEvents
//
//  Synopsis:   Connects to event log service
//
//  Arguments:  (none)
//
//  History:    1-03-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
InitializeEvents(void)
{
    hEventLog = RegisterEventSource(NULL, EventSourceName);
    if (hEventLog)
    {
        return(TRUE);
    }
    DebugLog((DEB_ERROR, "Could not open event log, error %d\n", GetLastError()));
    return(FALSE);
}

//+---------------------------------------------------------------------------
//
//  Function:   ReportServiceEvent
//
//  Synopsis:   Reports an event to the event log
//
//  Arguments:  [EventType]       -- EventType (ERROR, WARNING, etc.)
//              [EventId]         -- Event ID
//              [SizeOfRawData]   -- Size of raw data
//              [RawData]         -- Raw data
//              [NumberOfStrings] -- number of strings
//              ...               -- PWSTRs to string data
//
//  History:    1-03-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD
ReportServiceEvent(
    IN WORD EventType,
    IN DWORD EventId,
    IN DWORD SizeOfRawData,
    IN PVOID RawData,
    IN DWORD NumberOfStrings,
    ...
    )
{
    va_list arglist;
    ULONG i;
    PWSTR Strings[ MAX_EVENT_STRINGS ];
    DWORD rv;

    if (!hEventLog)
    {
        DebugLog((DEB_ERROR, "Cannot log event, no handle!\n"));
        return((DWORD)-1);
    }

    //
    // We're not supposed to be logging this, so nuke it
    //
    if ((LoggingLevel & (1 << EventType)) == 0)
    {
        return(0);
    }

    //
    // Look at the strings, if they were provided
    //
    va_start( arglist, NumberOfStrings );

    if (NumberOfStrings > MAX_EVENT_STRINGS) {
        NumberOfStrings = MAX_EVENT_STRINGS;
    }

    for (i=0; i<NumberOfStrings; i++) {
        Strings[ i ] = va_arg( arglist, PWSTR );
    }

    //
    // Report the event to the eventlog service
    //

    if (!ReportEvent(   hEventLog,
                        EventType,
                        0,            // event category
                        EventId,
                        NULL,
                        (WORD)NumberOfStrings,
                        SizeOfRawData,
                        Strings,
                        RawData) )
    {
        rv = GetLastError();
        DebugLog((DEB_ERROR,  "ReportEvent( %u ) failed - %u\n", EventId, GetLastError() ));

    }
    else
    {
        rv = ERROR_SUCCESS;
    }

    return rv;
}

BOOL
ShutdownEvents(void)
{
    return(DeregisterEventSource(hEventLog));
}


GLOBALS.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       globals.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#ifndef __GLOBALS_H__
#define __GLOBALS_H__

extern  WCHAR   BaseDirectory[];
extern  DWORD   LoggingLevel;

#endif


PARAM.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       param.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

WCHAR   szParamPath[] = TEXT("System\\CurrentControlSet\\Services\\Pop3Srv\\Parameters");
WCHAR   szMailDirValue[] = TEXT("Mail Directory");
WCHAR   szLoggingValue[] = TEXT("Logging Level");

//+---------------------------------------------------------------------------
//
//  Function:   ReadParameters
//
//  Synopsis:   Read parameters from registry
//
//  Arguments:  (none)
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
ReadParameters(VOID)
{
    HKEY    hKey;
    LONG    err;
    DWORD   ValueType;
    DWORD   SizeOfBuffer;

    if (err = RegOpenKey(HKEY_LOCAL_MACHINE, szParamPath, &hKey))
    {
        ReportServiceEvent(
            EVENTLOG_ERROR_TYPE,
            POP3EVENT_PARAMETER_MISSING,
            0, NULL, 1, szParamPath);
        DebugLog((DEB_ERROR, "Could not open key, %d\n", err));
        return(FALSE);
    }

    //
    // Now, read the parameters from the key:
    //

    SizeOfBuffer = MAX_PATH;
    err = RegQueryValueEx(  hKey,               // Key that we opened
                            szMailDirValue,     // Value that we want
                            NULL,
                            &ValueType,         // Receives value type
                            (PUCHAR) BaseDirectory,      // Receives value
                            &SizeOfBuffer );    // Size of buffer

    if (err || (ValueType != REG_SZ))
    {
        DebugLog((DEB_ERROR, "Error %d reading value\n", err));
        ReportServiceEvent(
            EVENTLOG_ERROR_TYPE,
            POP3EVENT_PARAMETER_MISSING,
            0, NULL, 1, szMailDirValue);
        RegCloseKey(hKey);
        return(FALSE);
    }

    //
    // Add a backslash, so life is easier later:
    //
    SizeOfBuffer = wcslen(BaseDirectory);
    if (BaseDirectory[SizeOfBuffer - 1] != L'\\')
    {
        BaseDirectory[SizeOfBuffer++] = L'\\';
        BaseDirectory[SizeOfBuffer] = L'\0';
    }

    SizeOfBuffer = sizeof(DWORD);
    err = RegQueryValueEx(  hKey,
                            szLoggingValue,
                            NULL,
                            &ValueType,
                            (PUCHAR) &LoggingLevel,
                            &SizeOfBuffer );

    if (err || ValueType != REG_DWORD)
    {
        DebugLog((DEB_ERROR, "Error %d reading value\n", err));
        ReportServiceEvent(
            EVENTLOG_ERROR_TYPE,
            POP3EVENT_PARAMETER_MISSING,
            0, NULL, 1, szLoggingValue);
        RegCloseKey(hKey);
        return(FALSE);
    }

    RegCloseKey(hKey);
    return(TRUE);
}


POP3.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       pop3.c
//
//  Contents:   This file contains the code to handle each pop command
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

char *  PopCommands[] = {   POP3_HELO,
                            POP3_USER,
                            POP3_PASS,
                            POP3_QUIT,
                            POP3_STAT,
                            POP3_LIST,
                            POP3_RETR,
                            POP3_DELE,
                            POP3_LAST,
                            POP3_RSET };

Pop3DispatchFn  Pop3DispatchTable[] = {
                            HeloDispatch,
                            UserDispatch,
                            PassDispatch,
                            QuitDispatch,
                            StatDispatch,
                            ListDispatch,
                            RetrDispatch,
                            DeleDispatch,
                            LastDispatch,
                            RsetDispatch };

#define POP3_STAT_RESPONSE  POP3_GOOD_RESPONSE " %d %d\r\n"
#define POP3_GOOD_RESPLINE  POP3_GOOD_RESPONSE "\r\n"
#define POP3_LIST_RESPLINE  "%d %d\r\n"
#define POP3_TERMINATOR     ".\r\n"
#define POP3_RETR_RESPONSE  POP3_GOOD_RESPONSE " %d octets\r\n"
#define POP3_LAST_RESPONSE  POP3_GOOD_RESPONSE " %d\r\n"

#define BANNER_STRING   POP3_GOOD_RESPONSE " POP3 Example Server Ready\r\n"
#define ERROR_POP3_INVALID_STATE     POP3_BAD_RESPONSE " Command not valid in this state\r\n"
#define ERROR_INVALID_USERNAME  POP3_BAD_RESPONSE " Username not valid\r\n"
#define ERROR_AUTH_FAILED       POP3_BAD_RESPONSE " Unable to log on\r\n"
#define ERROR_LOCK_FAILED_S     POP3_BAD_RESPONSE " Unable to lock directory\r\n"
#define ERROR_UNKNOWN_MESSAGE   POP3_BAD_RESPONSE " Unknown message\r\n"
#define ERROR_READ_FAILED_S     POP3_BAD_RESPONSE " Unable to read directory\r\n"
#define ERROR_SERVER_FAILURE    POP3_BAD_RESPONSE " General failure on server\r\n"

//+---------------------------------------------------------------------------
//
//  Function:   Pop3CrackCommand
//
//  Synopsis:   Takes an input buffer as received from a client, and cracks
//              it to determine the command to execute
//
//  Arguments:  [Request]    -- Input buffer
//              [RequestLen] -- Input buffer length
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
int
Pop3CrackCommand(
    PUCHAR      Request,
    DWORD       RequestLen)
{
    int i;

    if (RequestLen < 4)
    {
        return(-1);
    }

    for (i = 0; i < MAX_POP3_COMMAND ; i++ )
    {
        if (_strnicmp((PCHAR) Request, PopCommands[i], 4) == 0 )
        {
            return(i);
        }
    }

    return(-1);
}

//+---------------------------------------------------------------------------
//
//  Function:   CreatePop3Context
//
//  Synopsis:   Creates a client context to keep associated with the socket
//
//  Arguments:  (none)
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PVOID
CreatePop3Context(void)
{
    PPopContext pContext;

    pContext = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(PopContext));
    if (pContext)
    {
        pContext->State = PopAuthorization;
        ReportServiceEvent(
            EVENTLOG_INFORMATION_TYPE,
            POP3EVENT_USER_CONNECT,
            0, NULL, 0);
    }
    return(pContext);
}

//+---------------------------------------------------------------------------
//
//  Function:   DeletePop3Context
//
//  Synopsis:   Gets rid of the context when the socket closes
//
//  Arguments:  [pvContext] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
DeletePop3Context(PVOID pvContext)
{
    PPopContext pContext;

    pContext = (PPopContext) pvContext;

    if (pContext->pDirectory)
    {
        FreeMailDirectory(pContext->pDirectory);
        UnlockMailDirectory(pContext->hDirectoryLock);
        CloseHandle(pContext->hUserToken);
    }

    LocalFree(pContext);

}

//+---------------------------------------------------------------------------
//
//  Function:   AllocAndCopyString
//
//  Synopsis:   Copies a string into local alloc memory, so that socket code
//              can send it off, then free it.
//
//  Arguments:  [pszString] -- string to copy
//              [pcbString] -- returned length of string
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PUCHAR
AllocAndCopyString(
    PCHAR   pszString,
    PDWORD  pcbString)
{
    PCHAR   pszNewString;

    *pcbString = strlen(pszString);
    pszNewString = LocalAlloc(LMEM_FIXED, *pcbString + 1);
    if (pszNewString)
    {
        strcpy(pszNewString, pszString);
    }
    return((PUCHAR) pszNewString);
}

//+---------------------------------------------------------------------------
//
//  Function:   HeloDispatch
//
//  Synopsis:   Handles the HELO command, which, perversely, never gets sent
//              by most clients.
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
HeloDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    DebugLog((DEB_TRACE_PROT, "Received HELO\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if ((pContext->State == PopTransact) ||
        (pContext->State == PopUpdate) )
    {
        *OutputBuffer = ERROR_POP3_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_POP3_INVALID_STATE);
        return(Pop3_SendError);
    }

    //
    // Put us into Authorization state
    //
    pContext->State = PopAuthorization;

    //
    // No file, but copy the banner string and return it.
    //
    *SendHandle = NULL;
    *OutputBuffer = AllocAndCopyString(BANNER_STRING, OutputBufferLen);

    return(Pop3_SendBuffer);

}

//+---------------------------------------------------------------------------
//
//  Function:   UserDispatch
//
//  Synopsis:   Handles the USER command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
UserDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    char * Src;
    char * Dst;

    DebugLog((DEB_TRACE_PROT, "Received USER\n"));

    //
    // Verify context, and that the context says we can receive this command
    //
    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopAuthorization)
    {
        *OutputBuffer = ERROR_POP3_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_POP3_INVALID_STATE);
        return(Pop3_SendError);
    }

    if (InputBufferLen - POP3_COMMAND_LENGTH > USERNAME_LENGTH)
    {
        *OutputBuffer = ERROR_INVALID_USERNAME;
        *OutputBufferLen = sizeof(ERROR_INVALID_USERNAME);
        return(Pop3_SendError);
    }

    //
    // Copy in the user name without trailing \r or \n.
    //

    Src = InputBuffer + POP3_COMMAND_LENGTH;
    Dst = pContext->UserName;
    while (*Src && (*Src != '\r') && (*Src != '\n'))
    {
        *Dst++ = *Src++;
    }
    *Dst = '\0';

    //
    // Set the state so that we are expecting the password
    //
    pContext->State = PopAuthorization2;

    *SendHandle = NULL;
    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);

    return(Pop3_SendBuffer);

}

//+---------------------------------------------------------------------------
//
//  Function:   PassDispatch
//
//  Synopsis:   Handles the PASS command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
PassDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    CHAR    pass[MAX_PATH];
    char *  Dst;
    char *  Src;

    DebugLog((DEB_TRACE_PROT, "Received PASS\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopAuthorization2)
    {
        *OutputBuffer = ERROR_POP3_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_POP3_INVALID_STATE);
        return(Pop3_SendError);
    }

    //
    // Copy in the password without trailing \r or \n.
    //

    Src = InputBuffer + POP3_COMMAND_LENGTH;
    Dst = pass;
    while (*Src && (*Src != '\r') && (*Src != '\n'))
    {
        *Dst++ = *Src++;
    }
    *Dst = '\0';

#ifdef SECURE_BUILD

    //
    // Attempt to log the user on, meaning verify the name and password
    // provided, and get a token back.  The "." we're passing in means
    // use the domain if this machine is part of a domain, or use the local
    // accounts domain.
    //
    if (!LogonUserA(    pContext->UserName,
                        ".",
                        pass,
                        LOGON32_LOGON_INTERACTIVE,
                        LOGON32_PROVIDER_DEFAULT,
                        &pContext->hUserToken) )
    {
        pContext->State = PopAuthorization;
        ReportServiceEvent(
            EVENTLOG_WARNING_TYPE,
            POP3EVENT_LOGON_FAILURE,
            0, NULL,
            0);

        *OutputBuffer = ERROR_AUTH_FAILED;
        *OutputBufferLen = sizeof(ERROR_AUTH_FAILED);
        return(Pop3_SendError);
    }

#endif

    //
    // Now, open and read the mail drop directory
    //

    pContext->hDirectoryLock = LockMailDirectory(pContext->hUserToken,
                                                 pContext->UserName);

    if (!pContext->hDirectoryLock)
    {
        //
        // Could not lock directory.  Basically, we shut down this connection,
        // and let it go.
        //

        DebugLog((DEB_ERROR, "Unable to lock directory, %d\n", GetLastError()));

        pContext->State = PopAuthorization;

        //
        // Close the token.  This logs the user off.
        //
        CloseHandle(pContext->hUserToken);
        pContext->hUserToken = NULL;

        *OutputBuffer = ERROR_LOCK_FAILED_S;
        *OutputBufferLen = sizeof(ERROR_LOCK_FAILED_S);
        return(Pop3_SendError);
    }

    pContext->pDirectory = ReadMailDirectory(pContext->hUserToken,
                                             pContext->UserName);

    if (!pContext->pDirectory)
    {
        //
        // Couldn't read the directory.  So, we panic and
        // throw it all away.
        //

        DebugLog((DEB_ERROR, "Unable to read directory!\n"));

        pContext->State = PopAuthorization;
        UnlockMailDirectory(pContext->hDirectoryLock);
        pContext->hDirectoryLock = NULL;
        CloseHandle(pContext->hUserToken);

        *OutputBuffer = ERROR_READ_FAILED_S;
        *OutputBufferLen = sizeof(ERROR_LOCK_FAILED_S);
        return(Pop3_SendError);
    }

    pContext->State = PopTransact;
    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);
    return(Pop3_SendBuffer);

}

//+---------------------------------------------------------------------------
//
//  Function:   QuitDispatch
//
//  Synopsis:   Handles the QUIT command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
QuitDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{

    DebugLog((DEB_TRACE_PROT, "Received QUIT\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State == PopTransact)
    {
        pContext->State = PopUpdate;

        CommitMailDirectory(pContext->pDirectory, pContext->hUserToken);
        FreeMailDirectory(pContext->pDirectory);
        UnlockMailDirectory(pContext->hDirectoryLock);
        CloseHandle(pContext->hUserToken);
    }

    pContext->State = PopShutdown;

    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);

    return(Pop3_SendBuffer);

}

//+---------------------------------------------------------------------------
//
//  Function:   StatDispatch
//
//  Synopsis:   Handles the STAT command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
StatDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{

    char    StatResp[80];

    DebugLog((DEB_TRACE_PROT, "Received STAT\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_POP3_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_POP3_INVALID_STATE);
        return(Pop3_SendError);
    }

    //
    // Print the response into the buffer, then copy it and return it.
    //

    sprintf(StatResp, POP3_STAT_RESPONSE, pContext->pDirectory->cAvailMessages,
                        pContext->pDirectory->AvailSize);

    *OutputBuffer = AllocAndCopyString(StatResp, OutputBufferLen);
    *SendHandle = NULL;

    DebugLog((DEB_TRACE_PROT, "STAT: Sending %s\n", StatResp));

    return(Pop3_SendBuffer);

}

//+---------------------------------------------------------------------------
//
//  Function:   ListDispatch
//
//  Synopsis:   Handles the LIST command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
ListDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    PCHAR       pszBuffer;
    PCHAR       pszWalk;
    DWORD       TotalAlloc;
    DWORD       UsedAlloc;
    DWORD       CharCount;
    DWORD       SingleMessage;
    DWORD       i;
    char        SingleLine[80];

    DebugLog((DEB_TRACE_PROT, "Received LIST\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_POP3_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_POP3_INVALID_STATE);
        return(Pop3_SendError);
    }

    //
    // If there is a parameter, process it so we can find out what specific
    // message they want
    //

    if (InputBufferLen > POP3_COMMAND_LENGTH)
    {
        SingleMessage = (DWORD) -1;
        sscanf(&InputBuffer[POP3_COMMAND_LENGTH], " %d", &SingleMessage);

        //
        // Pop is 1-based, we're 0-based
        //
        SingleMessage--;

        if ((SingleMessage > pContext->pDirectory->cMessages) ||
            (pContext->pDirectory->Messages[SingleMessage].Flags & POP3_MESSAGE_DELETE))
        {
            *OutputBuffer = ERROR_UNKNOWN_MESSAGE;
            *OutputBufferLen = sizeof(ERROR_UNKNOWN_MESSAGE);
            return(Pop3_SendError);
        }

        sprintf(SingleLine, POP3_STAT_RESPONSE, SingleMessage + 1,
                    pContext->pDirectory->Messages[SingleMessage].Size);

        *OutputBuffer = AllocAndCopyString(SingleLine, OutputBufferLen);
        DebugLog((DEB_TRACE_PROT, "LIST: Sending %s", SingleLine));
        return(Pop3_SendBuffer);
    }

    //
    // If they want the whole list, send the whole list
    //
    TotalAlloc = pContext->pDirectory->cAvailMessages * 16 + 24;
    pszBuffer = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, TotalAlloc);
    if (!pszBuffer)
    {
        *OutputBuffer = ERROR_SERVER_FAILURE;
        *OutputBufferLen = sizeof(ERROR_SERVER_FAILURE);
        return(Pop3_SendError);

    }
    pszWalk = pszBuffer;
    UsedAlloc = 0;

    //
    // Copy the header
    //
    CharCount = sprintf(pszWalk, POP3_STAT_RESPONSE,
                        pContext->pDirectory->cAvailMessages,
                        pContext->pDirectory->AvailSize);

    pszWalk += CharCount;
    UsedAlloc += CharCount;
    for (i = 0; i < pContext->pDirectory->cMessages ; i++ )
    {
        //
        // For every message not marked deleted, copy it in
        //
        if ((pContext->pDirectory->Messages[i].Flags & POP3_MESSAGE_DELETE) == 0)
        {
            CharCount = sprintf(pszWalk, POP3_LIST_RESPLINE,
                                i+1, pContext->pDirectory->Messages[i].Size);
            pszWalk += CharCount;
            UsedAlloc += CharCount;
        }
    }

    strcpy(pszWalk, POP3_TERMINATOR);
    UsedAlloc += 3;

    *OutputBuffer = pszBuffer;
    *OutputBufferLen = UsedAlloc;

    DebugLog((DEB_TRACE_PROT, "LIST: Sending %s\n", pszBuffer));

    return(Pop3_SendBuffer);

}

//+---------------------------------------------------------------------------
//
//  Function:   RetrDispatch
//
//  Synopsis:   Handles the RETR command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
RetrDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    DWORD   MessageId;
    char    Line[40];

    DebugLog((DEB_TRACE_PROT, "Received RETR\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_POP3_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_POP3_INVALID_STATE);
        return(Pop3_SendError);
    }

    MessageId = (DWORD) -1;

    if (InputBufferLen > POP3_COMMAND_LENGTH)
    {
        sscanf(&InputBuffer[POP3_COMMAND_LENGTH], "%d", &MessageId);
    }

    //
    // Pop is 1-based, we're 0-based
    //
    MessageId--;

    if ((MessageId > pContext->pDirectory->cMessages) ||
        (pContext->pDirectory->Messages[MessageId].Flags & POP3_MESSAGE_DELETE))
    {
        *OutputBuffer = ERROR_UNKNOWN_MESSAGE;
        *OutputBufferLen = sizeof(ERROR_UNKNOWN_MESSAGE);
        return(Pop3_SendError);
    }

    //
    // Open the mail message.  We pass in the user's token, so that
    // we can impersonate when we do the open.
    //

    *SendHandle = OpenMailMessage(  MessageId,
                                    pContext->pDirectory,
                                    pContext->hUserToken);

    if (*SendHandle)
    {
        sprintf(Line, POP3_RETR_RESPONSE,
                pContext->pDirectory->Messages[MessageId].Size);
        *OutputBuffer = AllocAndCopyString(Line, OutputBufferLen);
        if (pContext->pDirectory->NextMessage < MessageId)
        {
            pContext->pDirectory->NextMessage = MessageId;
        }

        return(Pop3_SendBufferThenFile);

    }

    *OutputBuffer = ERROR_SERVER_FAILURE;
    *OutputBufferLen = sizeof(ERROR_SERVER_FAILURE);
    return(Pop3_SendError);
}

//+---------------------------------------------------------------------------
//
//  Function:   DeleDispatch
//
//  Synopsis:   Handles the DELE command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
DeleDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    DWORD   MessageId;

    DebugLog((DEB_TRACE_PROT, "Received DELE\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_POP3_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_POP3_INVALID_STATE);
        return(Pop3_SendError);
    }

    MessageId = (DWORD) -1;

    if (InputBufferLen > POP3_COMMAND_LENGTH)
    {
        sscanf(&InputBuffer[POP3_COMMAND_LENGTH], "%d", &MessageId);
    }

    //
    // Pop is 1-based, we're 0-based
    //
    MessageId--;

    if (MessageId > pContext->pDirectory->cMessages ||
        (pContext->pDirectory->Messages[MessageId].Flags & POP3_MESSAGE_DELETE))
    {
        *OutputBuffer = ERROR_UNKNOWN_MESSAGE;
        *OutputBufferLen = sizeof(ERROR_UNKNOWN_MESSAGE);
        return(Pop3_SendError);
    }

    //
    // Update directory with "deleted" file
    //
    pContext->pDirectory->Messages[MessageId].Flags |= POP3_MESSAGE_DELETE;
    pContext->pDirectory->cAvailMessages--;
    pContext->pDirectory->AvailSize -= pContext->pDirectory->Messages[MessageId].Size;

    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);
    return(Pop3_SendBuffer);
}

//+---------------------------------------------------------------------------
//
//  Function:   NoopDispatch
//
//  Synopsis:   Handles the NOOP command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
NoopDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{

    DebugLog((DEB_TRACE_PROT, "Received NOOP\n"));

    //
    // Verify context, and that the context says we can receive this command
    //

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_POP3_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_POP3_INVALID_STATE);
        return(Pop3_SendError);
    }

    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);
    return(Pop3_SendBuffer);
}

//+---------------------------------------------------------------------------
//
//  Function:   LastDispatch
//
//  Synopsis:   Handles the LAST command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
LastDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    char    Line[40];

    DebugLog((DEB_TRACE_PROT, "Received LAST\n"));

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_POP3_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_POP3_INVALID_STATE);
        return(Pop3_SendError);
    }

    sprintf(Line, POP3_LAST_RESPONSE, pContext->pDirectory->NextMessage+1);
    *OutputBuffer = AllocAndCopyString(Line, OutputBufferLen);

    return(Pop3_SendBuffer);
}

//+---------------------------------------------------------------------------
//
//  Function:   RsetDispatch
//
//  Synopsis:   Handles the RSET command
//
//  Arguments:  [pContext]        --
//              [InputBuffer]     --
//              [InputBufferLen]  --
//              [SendHandle]      --
//              [OutputBuffer]    --
//              [OutputBufferLen] --
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
RsetDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen)
{
    DWORD   i;

    DebugLog((DEB_TRACE_PROT, "Received LAST\n"));

    if (!pContext)
    {
        return(Pop3_Discard);
    }

    if (pContext->State != PopTransact)
    {
        *OutputBuffer = ERROR_POP3_INVALID_STATE;
        *OutputBufferLen = sizeof(ERROR_POP3_INVALID_STATE);
        return(Pop3_SendError);
    }

    for (i = 0; i < pContext->pDirectory->cMessages ; i++ )
    {
        pContext->pDirectory->Messages[i].Flags = 0;
    }

    pContext->pDirectory->NextMessage = 0;
    pContext->pDirectory->cAvailMessages = pContext->pDirectory->cMessages;
    pContext->pDirectory->AvailSize = pContext->pDirectory->TotalSize;

    *OutputBuffer = AllocAndCopyString(POP3_GOOD_RESPLINE, OutputBufferLen);
    return(Pop3_SendBuffer);
}

//+---------------------------------------------------------------------------
//
//  Function:   Pop3Dispatch
//
//  Synopsis:   Dispatch the command
//
//  Arguments:  [pContext]        --    Context for client connection
//              [InputBuffer]     --    Input Buffer (stuff from client)
//              [InputBufferLen]  --    How much stuff
//              [SendHandle]      --    Handle to Transmit, if so required
//              [OutputBuffer]    --    (returned) pointer of stuff to send
//              [OutputBufferLen] --    (returned) size of stuff to send
//
//  Returns:    a Pop3Disposition, which indicates what the socket code
//              should do with this stuff.  See pop3ctx.h for the dispositions
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
Pop3Disposition
Pop3Dispatch(
    PVOID       pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    )

{
    PPopContext pPopContext;
    int     Command;

    pPopContext = (PPopContext) pContext;

    //
    // Null terminate, so string functions won't get confused later.
    //

    InputBuffer[InputBufferLen] = L'\0';

    //
    // Figure the command out
    //

    Command = Pop3CrackCommand(InputBuffer, InputBufferLen);

    //
    // If it didn't parse, throw it away
    //
    if (Command == -1)
    {
        return(Pop3_Discard);
    }

    //
    // Let the command handlers do their thing
    //
    return(Pop3DispatchTable[Command](  pPopContext,
                                        InputBuffer,
                                        InputBufferLen,
                                        SendHandle,
                                        OutputBuffer,
                                        OutputBufferLen) );

}

#if DBG

//
// Interactive debug testing
//
void TestLoop(void)
{
    char    InputBuffer[256];
    DWORD   InputBufferLen;
    char *  OutputBuffer;
    DWORD   OutputBufferLen;
    HANDLE  hTest;
    PVOID   pContext;
    Pop3Disposition Disposition;
    extern WCHAR BaseDirectory[];

    printf("-------------------\n");
    printf("Interactive Test Mode\n");

    pContext = CreatePop3Context();
    do
    {
        gets(InputBuffer);
        InputBufferLen = strlen(InputBuffer);
        if (InputBufferLen)
        {
            Disposition = Pop3Dispatch( pContext,
                                        InputBuffer,
                                        InputBufferLen,
                                        &hTest,
                                        &OutputBuffer,
                                        &OutputBufferLen);

            switch (Disposition)
            {
                case Pop3_Discard:
                    printf("[Discard message]\n");
                    break;

                case Pop3_SendError:
                    printf("%s", OutputBuffer);
                    break;

                case Pop3_SendBuffer:
                    printf("%s", OutputBuffer);
                    LocalFree(OutputBuffer);
                    break;

                case Pop3_SendFile:
                    printf("Transmit file %x\n", hTest);
                    printf(".\r\n");
                    CloseHandle(hTest);
                    break;

                case Pop3_SendBufferThenFile:
                    printf("%s", OutputBuffer);
                    printf("Transmit file %x\n", hTest);
                    printf(".\r\n");
                    CloseHandle(hTest);
                    LocalFree(OutputBuffer);
                    break;

                case Pop3_SendFileThenBuffer:
                    printf("Transmit file %x\n", hTest);
                    printf("%s", OutputBuffer);
                    CloseHandle(hTest);
                    LocalFree(OutputBuffer);
                    break;

            }

        }

    } while (InputBufferLen);

}
#endif


POP3.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       pop3.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#ifndef __POP3_H__
#define __POP3_H__

////////////////////////////////////////////////////////////////////////////
//
//
//  Strings for the protocol:
//
//
////////////////////////////////////////////////////////////////////////////

#define POP3_GOOD_RESPONSE  "+OK"
#define POP3_BAD_RESPONSE   "-ERR"

#define POP3_SUCCESS(psz)   (*((char *)psz) == '+')
#define POP3_FAILURE(psz)   (*((char *)psz) == '-')

#define POP3_CONTINUE_RESPONSE  "+CONT"

#define POP3_HELO           "HELO"
#define POP3_USER           "USER"
#define POP3_PASS           "PASS"
#define POP3_QUIT           "QUIT"
#define POP3_STAT           "STAT"
#define POP3_LIST           "LIST"
#define POP3_RETR           "RETR"
#define POP3_DELE           "DELE"
#define POP3_NOOP           "NOOP"
#define POP3_LAST           "LAST"
#define POP3_RSET           "RSET"

#define POP3_EHLO           "EHLO"
#define POP3_AUTH           "AUTH"

#define POP3_HELO_ARG       ""
#define POP3_USER_ARG       "%s"
#define POP3_PASS_ARG       "%s"
#define POP3_QUIT_ARG       ""
#define POP3_STAT_ARG       ""
#define POP3_LIST_ARG       "%d"
#define POP3_RETR_ARG       "%d"
#define POP3_DELE_ARG       "%d"
#define POP3_NOOP_ARG       ""
#define POP3_LAST_ARG       ""
#define POP3_RSET_ARG       ""

#define POP3_EHLO_ARG       ""
#define POP3_AUTH_ARG       "%d"

#define POP3_HELO_ID        0
#define POP3_USER_ID        1
#define POP3_PASS_ID        2
#define POP3_QUIT_ID        3
#define POP3_STAT_ID        4
#define POP3_LIST_ID        5
#define POP3_RETR_ID        6
#define POP3_DELE_ID        7
#define POP3_NOOP_ID        8
#define POP3_LAST_ID        9
#define POP3_RSET_ID        10

#define POP3_EHLO_ID        11
#define POP3_AUTH_ID        12

#define MAX_POP3_COMMAND    11
#define POP3_COMMAND_LENGTH 5       // 4 chars and a space

#endif


POP3CTX.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       pop3ctx.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#ifndef __POP3CTX_H__
#define __POP3CTX_H__

#define POP3_MESSAGE_DELETE     0x00000001  // Message has been "deleted"

typedef enum _LAST_CLIENT_IO {
    ClientIoRead,
    ClientIoWrite,
    ClientIoTransmitFile
} LAST_CLIENT_IO, *PLAST_CLIENT_IO;

typedef struct _CLIENT_CONTEXT {
    SOCKET Socket;
    PVOID Context;
    LAST_CLIENT_IO LastClientIo;
    DWORD BytesReadSoFar;
    HANDLE TransmittedFile;
    HANDLE TransmittedBuffer;
    OVERLAPPED Overlapped;
    CHAR Buffer[8192];
} CLIENT_CONTEXT, *PCLIENT_CONTEXT;

typedef struct _PopMessageHeader {
    DWORD       Flags;                  // Flags about this message
    DWORD       Size;                   // Size of this message
    PWSTR       pszFileName;            // File name containing message
} PopMessageHeader, * PPopMessageHeader;

typedef struct _PopMailDirectory {
    DWORD               Flags;          // Flags about this directory
    DWORD               cMessages;      // Number of messages
    DWORD               NextMessage;    // Next message number
    DWORD               TotalSize;      // Total size of directory, in bytes
    DWORD               cAvailMessages; // Number of messages not deleted
    DWORD               AvailSize;      // Size of available messages
    PWSTR               pBaseDir;       // Base directory
    PPopMessageHeader   Messages;       // Array of message headers
} PopMailDirectory, * PPopMailDirectory;

typedef enum _PopState {
    PopNegotiate,
    PopAuthorization,                   // Authorization State (wait for USER)
    PopAuthorization2,                  // Waiting for PASS
    PopTransact,                        // Transact State
    PopUpdate,                          // Update state
    PopShutdown                         // Rundown state
} PopState, * PPopState;

#define USERNAME_LENGTH 32
typedef struct _PopContext {
    PopState            State;          // State of the connection
    HANDLE              hUserToken;     // Token for impersonation
    HANDLE              hDirectoryLock; // Lock for directory
    DWORD               LastError;      // Last error occurred
    DWORD               RetryCount;     // Number of retries
    PPopMailDirectory   pDirectory;     // Directory for retrieval
    CHAR                UserName[USERNAME_LENGTH];   // User Name for auth
} PopContext, * PPopContext;

typedef enum _Pop3Disposition {
    Pop3_Discard,                       // Discard the request
    Pop3_SendError,                     // Send the error string
    Pop3_SendBuffer,                    // Send the buffer returned
    Pop3_SendFile,                      // Send the File returned
    Pop3_SendBufferThenFile,            // Send the buffer, then the file
    Pop3_SendFileThenBuffer             // Send the file, then the buffer
} Pop3Disposition;

typedef Pop3Disposition
(* Pop3DispatchFn)(
    PPopContext pContext,               // Client's connection context
    PUCHAR      InputBuffer,            // Buffer sent from client
    DWORD       InputBufferLen,         // Size of buffer
    PHANDLE     SendHandle,             // Handle of file to send
    PUCHAR *    OutputBuffer,           // Output buffer to send
    PDWORD      OutputBufferLen         // Size of output buffer
    );

Pop3Disposition
HeloDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

Pop3Disposition
UserDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

Pop3Disposition
PassDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

Pop3Disposition
QuitDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

Pop3Disposition
StatDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

Pop3Disposition
ListDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

Pop3Disposition
RetrDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

Pop3Disposition
DeleDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

Pop3Disposition
LastDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

Pop3Disposition
RsetDispatch(
    PPopContext pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

#endif


POP3SRV.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       pop3svr.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

BOOL    fService = TRUE;
BOOL    fTestMode = FALSE;

//
// This table is the service dispatch table.  It lists the services in
// this process by name, and the entry point to invoke to start them.  This
// example has but one service, but this could be extended to include, for
// instance, an SMTP server.  Be sure to have the prototype for your entry
// point in a global header file, so that you don't get a compiler complaint.
//
SERVICE_TABLE_ENTRY Pop3SrvTable[] = {
            {APPLICATION_NAME, Pop3SrvMain},
            {NULL, NULL } };

//+---------------------------------------------------------------------------
//
//  Function:   DoArgs
//
//  Synopsis:   This parses the process's start arguments, basically just to
//              see if we have a -noservice flag so that we don't talk to the
//              service controller.
//
//  Arguments:  [argc] --
//              [argv] --
//
//  History:    1-09-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
void
DoArgs(
    int argc,
    char * argv[])
{
    int i;
    char * arg;

    for (i = 1; i < argc ; i++)
    {
        arg = argv[i];

        if (*arg == '-')
        {
            //
            // Ooo, an option.
            //
            if (_stricmp(arg, "-noservice") == 0)
            {
                fService = FALSE;
                continue;
            }
            if (_stricmp(arg, "-testmode") == 0)
            {
                fTestMode = TRUE;
                continue;
            }
        }
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   DoServiceController
//
//  Synopsis:   This calls into the service controller, and is never heard
//              from again.
//
//  Arguments:  (none)
//
//  Requires:
//
//  Returns:
//
//  History:    1-09-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
void
DoServiceController(void)
{
    StartServiceCtrlDispatcher(Pop3SrvTable);
}

//+---------------------------------------------------------------------------
//
//  Function:   main
//
//  Synopsis:   Process entry point.  Unless we're told to start without
//              the service controller, we quickly wait for a service start
//              command.
//
//  Arguments:  [argc] --
//              [argv] --
//
//  Requires:
//
//  Returns:
//
//  History:    1-09-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
void
main (int argc, char *argv[])
{

    //
    // Initialize the debug support, by reading our debug level from the
    // registry.
    //
    InitDebugSupport();

    //
    // Parse the arguments
    //
    DoArgs(argc, argv);

    //
    // If we're a service, go to the service controller, otherwise start
    // the service immediately.
    //
    if (fService)
    {
        DoServiceController();
    }
    else
    {
        Pop3SrvMain(argc, NULL);
    }

}


POP3SRVP.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       pop3svrp.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#include <windows.h>

//
// C-Runtimes
//

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include "debug.h"

//#define SECURE_BUILD

#include "pop3.h"
#include "pop3ctx.h"
#include "popfile.h"
#include "pop3evnt.h"

#include "protos.h"
#include "globals.h"

#define APPLICATION_NAME    TEXT("POP3Srv")


POPFILE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       popfile.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

#define POP3_LOCKFILE       TEXT("poplock")
#define POP3_LOCKFILE_NAME  TEXT("\\") POP3_LOCKFILE
#define POP3_SEARCH         TEXT("\\*.*")

WCHAR   BaseDirectory[MAX_PATH];

//+---------------------------------------------------------------------------
//
//  Function:   LockMailDirectory
//
//  Synopsis:   Attempts to lock the mail directory
//
//  Arguments:  [hUserToken] -- Token of user
//              [pszPath]    -- path to the mail directory
//
//  Returns:    Handle of lock file on success, or NULL
//
//  History:    1-06-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
HANDLE
LockMailDirectory(
    HANDLE          hUserToken,
    PCHAR           pszPath)
{

    WCHAR   PathToOpen[MAX_PATH];
    WCHAR   WidePath[64];
    HANDLE  hLockFile;

    //
    // Build lock file name
    //
    MultiByteToWideChar(
        CP_ACP, MB_PRECOMPOSED,
        pszPath, -1,
        WidePath, sizeof(WidePath) / sizeof(WCHAR) );

    wcscpy(PathToOpen, BaseDirectory);
    wcscat(PathToOpen, WidePath);
    wcscat(PathToOpen, POP3_LOCKFILE_NAME);

    //
    // Impersonate the client
    //
#ifdef SECURE_BUILD

    ImpersonateLoggedOnUser(hUserToken);

#endif

    //
    // Attempt to create the file.  This will fail if it already exists,
    // which would indicate that another thread/client is accessing this
    // mailbox.
    //
    DebugLog((DEB_TRACE, "Opening lock file as %ws\n", PathToOpen));

    hLockFile = CreateFile( PathToOpen,
                            GENERIC_READ | GENERIC_WRITE,
                            0,
                            NULL,
                            CREATE_NEW,
                            FILE_ATTRIBUTE_NORMAL |
                            FILE_FLAG_DELETE_ON_CLOSE,
                            NULL);

    //
    // Done being the client.  Back to server security
    //
    RevertToSelf();

    //
    // Return a handle, or NULL if failed
    //
    return(hLockFile != INVALID_HANDLE_VALUE ? hLockFile : NULL);

}

//+---------------------------------------------------------------------------
//
//  Function:   OpenMailMessage
//
//  Synopsis:   Opens the specified mail message as the user
//
//  Arguments:  [MessageId]  --
//              [pDir]       --
//              [hUserToken] --
//
//  History:    1-08-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
HANDLE
OpenMailMessage(
    DWORD               MessageId,
    PPopMailDirectory   pDir,
    HANDLE              hUserToken)
{
    HANDLE  hMailFile;
    WCHAR   Path[MAX_PATH];

    //
    // Impersonate the client
    //
#ifdef SECURE_BUILD

    ImpersonateLoggedOnUser(hUserToken);

#endif

    //
    // Attempt to create the file.  This will fail if it already exists,
    // which would indicate that another thread/client is accessing this
    // mailbox.
    //
    wcscpy(Path, pDir->pBaseDir);
    wcscat(Path, TEXT("\\"));
    wcscat(Path, pDir->Messages[MessageId].pszFileName);

    hMailFile = CreateFile( Path,
                            GENERIC_READ | GENERIC_WRITE,
                            0,
                            NULL,
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_HIDDEN,
                            NULL);

    //
    // Done being the client.  Back to server security
    //
    RevertToSelf();

    //
    // Return a handle, or NULL if failed
    //
    return(hMailFile != INVALID_HANDLE_VALUE ? hMailFile : NULL);
}

//+---------------------------------------------------------------------------
//
//  Function:   UnlockMailDirectory
//
//  Synopsis:   Unlocks the mail directory
//
//  Arguments:  [hLockFile] -- Handle of lock file to close
//
//  Returns:    TRUE if success, FALSE if failure
//
//  History:    1-06-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
UnlockMailDirectory(
    HANDLE      hLockFile)
{
    //
    // Since the lock file was opened with DELETE_ON_CLOSE semantics, it will
    // softly and silently vanish away when we close this, the one handle to
    // it.
    //

    return(CloseHandle(hLockFile));
}

//+---------------------------------------------------------------------------
//
//  Function:   ReadMailDirectory
//
//  Synopsis:   Reads in a list of all the files in the directory
//
//  Arguments:  [hUserToken] --
//              [pszPath]    --
//
//  Returns:
//
//  History:    1-08-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PPopMailDirectory
ReadMailDirectory(
    HANDLE          hUserToken,
    PCHAR           pszPath)
{
    WIN32_FIND_DATA     FindData;
    PPopMailDirectory   pDir;
    WCHAR               MailDir[MAX_PATH];
    WCHAR               WidePath[64];
    HANDLE              hSearch;
    PPopMessageHeader   pExtraHeaders;
    PPopMessageHeader   pHeader;
    DWORD               RemainingHeaders;

    MultiByteToWideChar(
        CP_ACP, MB_PRECOMPOSED,
        pszPath, -1,
        WidePath, sizeof(WidePath) / sizeof(WCHAR) );
    wcscpy(MailDir, BaseDirectory);
    wcscat(MailDir, WidePath);

    pDir = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(PopMailDirectory));
    pDir->pBaseDir = LocalAlloc(LMEM_FIXED, (wcslen(MailDir) + 1) * sizeof(WCHAR));
    wcscpy(pDir->pBaseDir, MailDir);

    wcscat(MailDir, POP3_SEARCH);

#ifdef SECURE_BUILD

    ImpersonateLoggedOnUser(hUserToken);

#endif

    hSearch = FindFirstFile(MailDir, &FindData);

    if (hSearch == INVALID_HANDLE_VALUE)
    {
        DebugLog((DEB_TRACE, "FindFirst FAILED!, %d\n", GetLastError()));
        RevertToSelf();

        return(NULL);
    }

    pExtraHeaders = LocalAlloc(LMEM_FIXED, sizeof(PopMessageHeader) * 10);
    RemainingHeaders = 10;
    pHeader = pExtraHeaders;

    do
    {
        if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
                                        FILE_ATTRIBUTE_SYSTEM |
                                        FILE_ATTRIBUTE_HIDDEN) )
        {
            continue;   // Skip non-normal files
        }
        if (wcsicmp(FindData.cFileName,POP3_LOCKFILE) == 0)
        {
            continue;   // Skip our lock file
        }
        DebugLog((DEB_TRACE, "ReadDir:  Message %ws, size %d\n",
                FindData.cFileName, FindData.nFileSizeLow));

        pHeader->Flags = 0;
        pDir->TotalSize += FindData.nFileSizeLow;
        pHeader->Size = FindData.nFileSizeLow;

        pHeader->pszFileName = LocalAlloc(LMEM_FIXED,
                            (wcslen(FindData.cFileName) + 1) * sizeof(WCHAR) );

        wcscpy(pHeader->pszFileName, FindData.cFileName);
        pHeader++;
        RemainingHeaders--;

        if (RemainingHeaders == 0)
        {
            PPopMessageHeader   pTemp;

            pTemp = LocalAlloc(LMEM_FIXED, sizeof(PopMessageHeader) * (pDir->cMessages + 20));

            CopyMemory(pTemp, pExtraHeaders, sizeof(PopMessageHeader) * (pDir->cMessages + 10));

            LocalFree(pExtraHeaders);

            pExtraHeaders = pTemp;

            RemainingHeaders = 10;

            pDir->cMessages += 10;
        }

    } while (FindNextFile(hSearch, &FindData));

    pDir->cMessages += (10 - RemainingHeaders);
    pDir->Messages = pExtraHeaders;

    FindClose(hSearch);

    pDir->cAvailMessages = pDir->cMessages;
    pDir->AvailSize = pDir->TotalSize;

    RevertToSelf();

    return(pDir);
}

BOOL
CommitMailDirectory(
    PPopMailDirectory   pDir,
    HANDLE              hUserToken)
{
    DWORD   i;
    WCHAR   Path[MAX_PATH];
    PWSTR   pszPath;

    wcscpy(Path, pDir->pBaseDir);
    wcscat(Path, TEXT("\\"));
    pszPath = Path + wcslen(Path);

#ifdef SECURE_BUILD

    ImpersonateLoggedOnUser(hUserToken);

#endif

    for (i = 0; i < pDir->cMessages ; i++)
    {
        if (pDir->Messages[i].Flags & POP3_MESSAGE_DELETE)
        {
            wcscpy(pszPath, pDir->Messages[i].pszFileName);
            DeleteFile(Path);
        }
    }

    RevertToSelf();

    return(TRUE);
}

void
FreeMailDirectory(
    PPopMailDirectory   pDir)
{
    DWORD   i;

    for (i = 0 ; i < pDir->cMessages ; i++ )
    {
        LocalFree(pDir->Messages[i].pszFileName);
    }

    LocalFree(pDir->Messages);
    LocalFree(pDir);

}


POPFILE.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       popfile.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#ifndef __POPFILE_H__
#define __POPFILE_H__

HANDLE
LockMailDirectory(
    HANDLE          hUserToken,         // Token of user
    PCHAR           pszPath);           // Path to mail directory

BOOL
UnlockMailDirectory(
    HANDLE          hLockFile);

PPopMailDirectory
ReadMailDirectory(
    HANDLE          hUserToken,
    PCHAR           pszPath);

BOOL
CommitMailDirectory(
    PPopMailDirectory   pDir,
    HANDLE              hUserToken);

void
FreeMailDirectory(
    PPopMailDirectory   pDir);

HANDLE
OpenMailMessage(
    DWORD               MessageId,
    PPopMailDirectory   pDir,
    HANDLE              hUserToken);

#endif


PROTOS.H

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       protos.h
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#ifndef __PROTOS_H__
#define __PROTOS_H__

//
// Service Controller interaction:
//

BOOL
NotifyServiceController(
            VOID);

BOOL
UpdateServiceStatus(DWORD   Status);

void
FailServiceStart(
    DWORD           Win32Code,
    DWORD           PrivateCode);

//
// Event Logging Support
//

BOOL
InitializeEvents(void);

DWORD
ReportServiceEvent(
    IN WORD EventType,
    IN DWORD EventId,
    IN DWORD SizeOfRawData,
    IN PVOID RawData,
    IN DWORD NumberOfStrings,
    ...
    );

//
// Command dispatcher
//

Pop3Disposition
Pop3Dispatch(
    PVOID       pContext,
    PUCHAR      InputBuffer,
    DWORD       InputBufferLen,
    PHANDLE     SendHandle,
    PUCHAR *    OutputBuffer,
    PDWORD      OutputBufferLen
    );

PVOID
CreatePop3Context(void);

//
// Parameter support:
//

BOOL
ReadParameters(VOID);

//
// Service Support
//

void
WINAPI
Pop3SrvMain(
    DWORD       argc,
    LPTSTR      argv[]);

//
// Worker thread initialization routine.
//

HANDLE
InitializeThreads (
    VOID
    );

//
// Connection management routines.
//

BOOL
AcceptClients (
    HANDLE hCompletionPort
    );

VOID
CloseClient (
    PCLIENT_CONTEXT lpClientContext,
    BOOL bGraceful
    );

#endif


DEBUG.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       debug.c
//
//  Contents:   Debugging support functions
//
//  Classes:
//
//  Functions:
//
//  Note:       This file is not compiled for retail builds
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

#if DBG         // NOTE:  This file not compiled for retail builds

FILE *  LogFile;
DWORD   PopInfoLevel = 15;

// Debugging support functions.

char * DebLevel[] = {   "Pop3Srv-Error",
                        "Pop3Srv-Warn",
                        "Pop3Srv-Trace",
                        "Pop3Srv-Trace-Protocol"
                    };

typedef struct _DebugKeys {
    char *  Name;
    DWORD   Value;
} DebugKeys, *PDebugKeys;

DebugKeys   DebugKeyNames[] = {
                {"Error",       DEB_ERROR},
                {"Warning",     DEB_WARN},
                {"Trace",       DEB_TRACE},
                {"Pool",        DEB_TRACE_PROT},
                };

#define NUM_DEBUG_KEYS  sizeof(DebugKeyNames) / sizeof(DebugKeys)
#define NUM_BREAK_KEYS  sizeof(BreakKeyNames) / sizeof(DebugKeys)

//+---------------------------------------------------------------------------
//
//  Function:   LogEvent
//
//  Synopsis:   Logs an event to the console and, optionally, a file.
//
//  Effects:
//
//  Arguments:  [Mask]   --
//              [Format] --
//              [Format] --
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    4-29-93   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

void
LogEvent(   long            Mask,
            const char *    Format,
            ...)
{
    va_list ArgList;
    int     Level = 0;
    int     PrefixSize = 0;
    char    szOutString[256];
    long    OriWinlogonlMask = Mask;

    if (Mask & PopInfoLevel)
    {
        while (!(Mask & 1))
        {
            Level++;
            Mask >>= 1;
        }
        if (Level >= (sizeof(DebLevel) / sizeof(char *)) )
        {
            Level = (sizeof(DebLevel) / sizeof(char *)) - 1;
        }

        //
        // Make the prefix first:  "Process.Thread> Pop3Svr-XXX"
        //

        PrefixSize = sprintf(szOutString, "%d.%d> %s: ",
                GetCurrentProcessId(), GetCurrentThreadId(), DebLevel[Level]);

        va_start(ArgList, Format);

        if (_vsnprintf(&szOutString[PrefixSize], sizeof(szOutString) - PrefixSize,
                            Format, ArgList) < 0)
        {
            //
            // Less than zero indicates that the string could not be
            // fitted into the buffer.  Output a special message indicating
            // that:
            //

            OutputDebugStringA("Pop3Svr!LogEvent:  Could not pack string into 256 bytes\n");

        }
        else
        {
            OutputDebugStringA(szOutString);
        }

        if (LogFile)
        {
            SYSTEMTIME  stTime;

            GetLocalTime(&stTime);
            fprintf(LogFile, "%02d:%02d:%02d.%03d: %s\n",
                    stTime.wHour, stTime.wMinute, stTime.wSecond,
                    stTime.wMilliseconds, szOutString);

            fflush(LogFile);
        }

    }

}

void
DebugOpenLogFile(LPSTR   pszLogFile)
{
    LogFile = fopen(pszLogFile, "a");
    if (!LogFile)
    {
        OutputDebugStringA("Pop3Svr: Could not open logfile for append");
        OutputDebugStringA(pszLogFile);
    }
    DebugLog((DEB_TRACE, "Log file '%s' begins\n", pszLogFile));
}

DWORD
GetDebugKeyValue(
    PDebugKeys      KeyTable,
    int             cKeys,
    LPSTR           pszKey)
{
    int     i;

    for (i = 0; i < cKeys ; i++ )
    {
        if (_stricmp(KeyTable[i].Name, pszKey) == 0)
        {
            return(KeyTable[i].Value);
        }
    }
    return(0);
}

//+---------------------------------------------------------------------------
//
//  Function:   LoadDebugParameters
//
//  Synopsis:   Loads debug parameters from win.ini
//
//  Effects:
//
//  Arguments:  (none)
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    4-29-93   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

void
LoadDebugParameters(char * szSection)
{
    char    szVal[128];
    char *  pszDebug;
    int     cbVal;

    cbVal = GetProfileStringA(szSection, "DebugFlags", "Error,Warning", szVal, sizeof(szVal));

    pszDebug = strtok(szVal, ", \t");
    while (pszDebug)
    {
        PopInfoLevel |= GetDebugKeyValue(DebugKeyNames, NUM_DEBUG_KEYS, pszDebug);
        pszDebug = strtok(NULL, ", \t");
    }

    cbVal = GetProfileStringA(szSection, "LogFile", "", szVal, sizeof(szVal));
    if (cbVal)
    {
        DebugOpenLogFile(szVal);
    }

}

//+---------------------------------------------------------------------------
//
//  Function:   InitDebugSupport
//
//  Synopsis:   Initializes debugging support for the Winlogon
//
//  Effects:
//
//  Arguments:  (none)
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    4-29-93   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

void
InitDebugSupport(void)
{
    LoadDebugParameters("Pop");
    LoadDebugParameters("Winlogon");

}

#else // DBG

#pragma warning(disable:4206)   // Disable the empty transation unit
                                // warning/error

#endif  // NOTE:  This file not compiled for retail builds


SERVICE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       service.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

SERVICE_STATUS_HANDLE   hService = 0;
WCHAR                   wszServiceName[] = APPLICATION_NAME;
SERVICE_STATUS          ServiceStatus;

extern  BOOLEAN fService;
extern  BOOLEAN fTestMode;
#if DBG
extern  void TestLoop(void);
#endif

SOCKET sListener;
HANDLE  hCompletionPort;
WSADATA WsaData;
BOOL bServiceTerminating = FALSE;

//+---------------------------------------------------------------------------
//
//  Function:   ServiceControlHandler
//
//  Synopsis:   Handles requests from the service controller.
//
//  Arguments:  [fdwControl] -- Request code
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
WINAPI
ServiceControlHandler(
    DWORD           fdwControl)
{
    switch (fdwControl)
    {
        case SERVICE_CONTROL_STOP:

            UpdateServiceStatus(SERVICE_STOP_PENDING);

            //
            // Remember that the service is terminating.
            //

            bServiceTerminating = TRUE;

            //
            // Close the completion port and the listening socket.
            // These actions will cause the other threads to exit.
            //

            closesocket( sListener );
            CloseHandle( hCompletionPort );

            UpdateServiceStatus(SERVICE_STOPPED);
            return;

        case SERVICE_CONTROL_INTERROGATE:
            UpdateServiceStatus(ServiceStatus.dwCurrentState);
            return;

        default:
            return;
    }

}

//+---------------------------------------------------------------------------
//
//  Function:   NotifyServiceController
//
//  Synopsis:   Notifies the service controller of our control entry point,
//              and tells it that we're trying to start up.
//
//  Arguments:  (none)
//
//  Algorithm:
//
//  History:    1-11-95   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
NotifyServiceController(
            VOID)
{
    if (!fService)
    {
        return(TRUE);
    }
    ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    ServiceStatus.dwWin32ExitCode = 0;
    ServiceStatus.dwServiceSpecificExitCode = 0;
    ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;

    hService = RegisterServiceCtrlHandler(wszServiceName, ServiceControlHandler);

    if (hService)
    {
        UpdateServiceStatus(SERVICE_START_PENDING);
        return(TRUE);
    }
    else
        return(FALSE);

}

BOOL
UpdateServiceStatus(DWORD   Status)
{
    if (hService)
    {
        ServiceStatus.dwCurrentState = Status;
        if ((Status == SERVICE_START_PENDING) || (Status == SERVICE_STOP_PENDING))
        {
            ServiceStatus.dwCheckPoint ++;
            ServiceStatus.dwWaitHint = 5000;    // 5 sec.
        }
        else
        {
            ServiceStatus.dwCheckPoint = 0;
            ServiceStatus.dwWaitHint = 0;
        }

        return(SetServiceStatus(hService, &ServiceStatus));
    }

    return(FALSE);
}

void
FailServiceStart(
    DWORD           Win32Code,
    DWORD           PrivateCode)
{
    ServiceStatus.dwWin32ExitCode = Win32Code;
    ServiceStatus.dwServiceSpecificExitCode = PrivateCode;
    UpdateServiceStatus(SERVICE_STOPPED);

}

void
WINAPI
Pop3SrvMain(
    DWORD       argc,
    LPTSTR      argv[])
{
    int error;

    if (!NotifyServiceController())
    {
        DebugLog((DEB_ERROR, "Could not contact service controller\n"));
        return;
    }

    if (!InitializeEvents())
    {
        DebugLog((DEB_ERROR, "Could not initialize events\n"));
        FailServiceStart(GetLastError(), 0);
        return;
    }

    UpdateServiceStatus(SERVICE_START_PENDING);

    if (!ReadParameters())
    {
        DebugLog((DEB_ERROR, "Could not read parameters!\n"));
        FailServiceStart(GetLastError(), 0);
        return;
    }

    UpdateServiceStatus(SERVICE_START_PENDING);

    ReportServiceEvent(
        EVENTLOG_INFORMATION_TYPE,
        POP3EVENT_SERVICE_STARTED,
        0, NULL, 0);

    if (fTestMode)
    {
#if DBG
        TestLoop();
#endif
    }
    else
    {

        error = WSAStartup( 0x0101, &WsaData );
        if ( error == SOCKET_ERROR ) {
            printf( "WSAStartup failed.\n" );
        }

        UpdateServiceStatus(SERVICE_START_PENDING);

        //
        // Initialize the POP3SRV worker threads.
        //

        hCompletionPort = InitializeThreads( );
        if ( hCompletionPort == NULL ) {
            printf( "it failed.\n" );
        }

        UpdateServiceStatus(SERVICE_RUNNING);

        //
        // Start accepting and processing clients.
        //

        AcceptClients( hCompletionPort );
    }
}


SOCKET.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       pop3svr.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

extern BOOL bServiceTerminating;
extern SOCKET sListener;

#define POP3_PORT 110

PCHAR Welcome = "+OK Welcome to the NT Example POP3 Server (POP3SRV)\r\n";

BOOL
AcceptClients (
    HANDLE hCompletionPort
    )

/*++

Routine Description:

    This routine sets up a listening socket on the POP3 port, then
    loops accepting incoming connections from clients.  Whenever
    it gets a connection, it sends the greeting message and starts
    a read on the newly connected socket.

Arguments:

    hCompletionPort - the completion port that the POP3SRV worker
        threads are waiting on.

Return Value:

    BOOL - FALSE if there was an error in starting the listening
        socket.

--*/

{
    SOCKET s;
    SOCKADDR_IN sin;
    int err;
    BOOL bSuccess;
    PCLIENT_CONTEXT lpClientContext;
    DWORD dwBytesRead;
    int zero;

    //
    // Create a listening socket that we'll use to accept incoming
    // conections.
    //

    sListener = socket( AF_INET, SOCK_STREAM, 0 );
    if ( sListener == INVALID_SOCKET ) {
        return FALSE;
    }

    //
    // Bind the socket to the POP3 well-known port.
    //

    sin.sin_family = AF_INET;
    sin.sin_port = htons( POP3_PORT );
    sin.sin_addr.s_addr = INADDR_ANY;

    err = bind( sListener, (LPSOCKADDR)&sin, sizeof(sin) );
    if ( err == SOCKET_ERROR ) {
        closesocket( sListener );
        return FALSE;
    }

    //
    // Listen for incoming connections on the socket.
    //

    err = listen( sListener, 5 );
    if ( err == SOCKET_ERROR ) {
        closesocket( sListener );
        return FALSE;
    }

    //
    // Loop forever accepting connections from clients.
    //

    while ( TRUE ) {

        s = accept( sListener, NULL, NULL );
        if ( s == INVALID_SOCKET ) {
            closesocket( sListener );
            return FALSE;
        }

        //
        // If the service if terminating, exit this thread.
        //

        if ( bServiceTerminating ) {
            return FALSE;
        }

        //
        // Allocate a socket context for the new connection.  We use
        // this context structure to track socket-level information
        // about the client.
        //

        lpClientContext = LocalAlloc( 0, sizeof(*lpClientContext) );
        if ( lpClientContext == NULL ) {
            closesocket( s );
            continue;
        }

        //
        // Initialize the context structure.
        //

        lpClientContext->Socket = s;
        lpClientContext->Context = CreatePop3Context( );
        lpClientContext->Overlapped.Internal = 0;
        lpClientContext->Overlapped.InternalHigh = 0;
        lpClientContext->Overlapped.Offset = 0;
        lpClientContext->Overlapped.OffsetHigh = 0;
        lpClientContext->Overlapped.hEvent = NULL;

        //
        // Send the welcome banner.
        //

        err = send( s, Welcome, strlen(Welcome), 0 );
        if ( err == SOCKET_ERROR ) {
            LocalFree( lpClientContext );
            closesocket( s );
            continue;
        }

        // --- DavidTr: Slide 14(a) ----------------------------------------
        //
        // Associate the new socket with a completion port.
        //

        hCompletionPort = CreateIoCompletionPort(
                              (HANDLE)s,
                              hCompletionPort,
                              (DWORD)lpClientContext,
                              0
                              );
        if ( hCompletionPort == NULL ) {
            LocalFree( lpClientContext );
            closesocket( s );
            closesocket( sListener );
            return FALSE;
        }

        // --- DavidTr: Slide 16 -------------------------------------------
        //
        // Disable send bufferring on the socket.  Setting SO_SNDBUF
        // to 0 causes winsock to stop bufferring sends and perform
        // sends directly from our buffers, thereby reducing CPU
        // usage.
        //

        zero = 0;
        err = setsockopt( s, SOL_SOCKET, SO_SNDBUF, (char *)&zero, sizeof(zero) );
        if ( err == SOCKET_ERROR ) {
            LocalFree( lpClientContext );
            closesocket( s );
            closesocket( sListener );
            return FALSE;
        }

        //
        // Start an overlapped read on the socket.  This read will
        // complete in one of the worker threads, which will continue
        // processing of the client.
        //

        lpClientContext->LastClientIo = ClientIoRead;
        lpClientContext->BytesReadSoFar = 0;

        bSuccess = ReadFile(
                       (HANDLE)s,
                       lpClientContext->Buffer,
                       sizeof(lpClientContext->Buffer),
                       &dwBytesRead,
                       &lpClientContext->Overlapped
                       );
        if ( !bSuccess && GetLastError( ) != ERROR_IO_PENDING ) {
            LocalFree( lpClientContext );
            closesocket( s );
            continue;
        }
    }

} // AcceptClients

VOID
CloseClient (
    PCLIENT_CONTEXT lpClientContext,
    BOOL bGraceful
    )

/*++

Routine Description:

    Terminates a connection with a client and frees up resources.

Arguments:

    lpClientContext - a pointer to the context structure that contains
        information about this client.

    bGraceful - TRUE if the close should be graceful, FALSE otherwise.

Return Value:

    None.

--*/

{
    LINGER lingerStruct;

    //
    // If we're supposed to abort the connection, set the linger value
    // on the socket to 0.
    //

    if ( !bGraceful ) {
        lingerStruct.l_onoff = 1;
        lingerStruct.l_linger = 0;
        setsockopt( lpClientContext->Socket, SOL_SOCKET, SO_LINGER,
                    (char *)&lingerStruct, sizeof(lingerStruct) );
    }

    //
    // Now close the socket handle.  This will do an abortive or
    // graceful close, as requested.
    //

    closesocket( lpClientContext->Socket );

    //
    // Free context structuyres.
    //

    LocalFree( lpClientContext->Context );
    LocalFree( lpClientContext );

    return;

} // CloseClient


THREADS.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright 1992 - 1998 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/

//+---------------------------------------------------------------------------
//
//  File:       pop3.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//----------------------------------------------------------------------------

#include "pop3srvp.h"
#pragma hdrstop

extern BOOL bServiceTerminating;

DWORD
WINAPI
WorkerThread (
    LPVOID WorkContext
    );

HANDLE
InitializeThreads (
    VOID
    )

/*++

Routine Description:

    Starts up the POP3SRV worker threads.  We use two worker threads
    for eachprocessor on the system--this is choosen as a good balance
    that ensures that there are a sufficient number of threads available
    to get useful work done but not too many that context switches
    consume significant overhead.

Arguments:

    None.

Return Value:

    HANDLE - A handle to the completion port if everything was
        successful, or NULL if there was a failure.

--*/

{

    SOCKET s;
    DWORD i;
    HANDLE hCompletionPort;
    HANDLE hThreadHandle;
    DWORD dwThreadId;
    SYSTEM_INFO systemInfo;

    //
    // First open a temporary socket that we will use to create the
    // completion port.  In NT 3.51 it will not be necessary to specify
    // the FileHandle parameter of CreateIoCompletionPort()--it will
    // be legal to specify FileHandle as NULL.  However, for NT 3.5
    // we need an overlapped file handle.
    //

    s = socket( AF_INET, SOCK_DGRAM, 0 );
    if ( s == INVALID_SOCKET ) {
        return NULL;
    }

    //
    // Create the completion port that will be used by all the worker
    // threads.
    //

    hCompletionPort = CreateIoCompletionPort( (HANDLE)s, NULL, 0, 0 );
    if ( hCompletionPort == NULL ) {
        closesocket( s );
        return NULL;
    }

    //
    // Close the socket, we don't need it any longer.
    //

    closesocket( s );

    //
    // Determine how many processors are on the system.
    //

    GetSystemInfo( &systemInfo );

    //
    // Create worker threads that will service the actual overlapped
    // I/O requests.  Create two worker threads for each processor
    // on the system.
    //

    for ( i = 0; i < systemInfo.dwNumberOfProcessors*2; i++ ) {

        hThreadHandle = CreateThread(
                            NULL,
                            0,
                            WorkerThread,
                            hCompletionPort,
                            0,
                            &dwThreadId
                            );
        if ( hThreadHandle == NULL ) {
            CloseHandle( hCompletionPort );
            return NULL;
        }

        //
        // Close each thread handle as we open them.  We do not need
        // the thread handles.  Note that each thread will continue
        // executing.
        //

        CloseHandle( hThreadHandle );
    }

    //
    // All was successful.
    //

    return hCompletionPort;

} // InitializeThreads

DWORD
WINAPI
WorkerThread (
    LPVOID WorkContext
    )

/*++

Routine Description:

    This is the main worker routine for the POP3SRV worker threads.
    Worker threads wait on a completion port for I/O to complete.  When
    it completes, the worker thread processes the I/O, then either pends
    new I/O or closes the client's connection.  When the service shuts
    down, other code closes the completion port which causes
    GetQueuedCompletionStatus() to wake up and the worker thread then
    exits.

Arguments:

    WorkContext - the completion port handle that will get I/O completion
        notifications.

Return Value:

    DWORD - status of the thread.

--*/

{
    HANDLE hCompletionPort = WorkContext;
    BOOL bSuccess;
    DWORD dwIoSize;
    LPOVERLAPPED lpOverlapped;
    PCLIENT_CONTEXT lpClientContext;
    Pop3Disposition Disposition;
    HANDLE hFile;
    CHAR * OutputBuffer;
    DWORD OutputBufferLen;
    TRANSMIT_FILE_BUFFERS TranfileBuffers;

    //
    // Loop servicing I/O completions.
    //

    while ( TRUE ) {

        // --- DavidTr: Slide 14(b) ------------------------------------------
        //
        // Get a completed IO request.
        //

        bSuccess = GetQueuedCompletionStatus(
                       hCompletionPort,
                       &dwIoSize,
                       (LPDWORD)&lpClientContext,
                       &lpOverlapped,
                       (DWORD)-1
                       );

        //
        // If the service is terminating, exit this thread.
        //

        if ( bServiceTerminating ) {
            return 0;
        }

        //
        // If the IO failed, close the socket and free context.
        //

        if ( !bSuccess ) {
            CloseClient( lpClientContext, FALSE );
            continue;
        }

        //
        // If the request was a read, process the client request.
        //

        if ( lpClientContext->LastClientIo == ClientIoRead ) {

            //
            // BUGBUG: if this were a real production piece of code,
            // we would check here for an incomplete read.  Because
            // TCP/IP is a stream oriented protocol, it is feasible
            // that we could receive part of a client request.
            // Therefore, we should check for the CRLF that ends a
            // client request.
            //

            //
            // Process the request.  Pop3Dispatch() handles all aspects
            // of the request and tells us how to respond to the client.
            //

            Disposition = Pop3Dispatch(
                              lpClientContext->Context,
                              lpClientContext->Buffer,
                              dwIoSize,
                              &hFile,
                              &OutputBuffer,
                              &OutputBufferLen
                              );

            //
            // Act based on the Disposition.
            //

            switch ( Disposition ) {

            case Pop3_Discard:
                break;

            case Pop3_SendError:
            case Pop3_SendBuffer:

                // --- DavidTr: Slide 7(a) -----------------------------------
                //
                // Set up context information and perform an overlapped
                // write on the socket.
                //

                lpClientContext->LastClientIo = ClientIoWrite;
                lpClientContext->TransmittedBuffer = OutputBuffer;

                bSuccess = WriteFile(
                               (HANDLE)lpClientContext->Socket,
                               OutputBuffer,
                               OutputBufferLen,
                               &dwIoSize,
                               &lpClientContext->Overlapped
                               );
                if ( !bSuccess && GetLastError( ) != ERROR_IO_PENDING ) {
                    CloseClient( lpClientContext, FALSE );
                    continue;
                }

                //
                // Continue looping to get completed IO requests--we
                // do not want to pend another read now.
                //

                continue;

            case Pop3_SendFile:
            case Pop3_SendBufferThenFile:

                //
                // Determine based on the disposition whether we will
                // need to send a head or tail buffer.
                //

                if ( Disposition == Pop3_SendFile ) {
                    TranfileBuffers.Head = NULL;
                    TranfileBuffers.HeadLength = 0;
                } else if ( Disposition == Pop3_SendBufferThenFile ) {
                    TranfileBuffers.Head = OutputBuffer;
                    TranfileBuffers.HeadLength = OutputBufferLen;
                }

                //
                // After the file, we're going to send a .CRLF sequence
                // so that the client detects EOF.  Note that
                // TransmitFile() will send this terminator in the same
                // packet as the last chunk of the file, thereby saving
                // network traffic.
                //

                TranfileBuffers.Tail = ".\r\n";
                TranfileBuffers.TailLength = 3;

                //
                // Set up context for the I/O so that we know how to act
                // when the I/O completes.
                //

                lpClientContext->LastClientIo = ClientIoTransmitFile;
                lpClientContext->TransmittedFile = hFile;
                lpClientContext->TransmittedBuffer = OutputBuffer;

                // --- DavidTr: Slide 21 ---------------------------------
                //
                // Now transmit the file and the data buffers.
                //

                bSuccess = TransmitFile(
                               lpClientContext->Socket,
                               hFile,
                               0,
                               0,
                               &lpClientContext->Overlapped,
                               &TranfileBuffers,
                               0
                               );
                if ( !bSuccess && GetLastError( ) != ERROR_IO_PENDING ) {
                    CloseClient( lpClientContext, FALSE );
                    continue;
                }

                //
                // Continue looping to get completed IO requests--we
                // do not want to pend another read now.
                //

                continue;
            }

        } else if ( lpClientContext->LastClientIo == ClientIoWrite ) {

            //
            // Clean up after the WriteFile().
            //

            LocalFree( lpClientContext->TransmittedBuffer );

        } else if ( lpClientContext->LastClientIo == ClientIoTransmitFile ) {

            //
            // Clean up after the TransmitFile().
            //

            CloseHandle( lpClientContext->TransmittedFile );
            LocalFree( lpClientContext->TransmittedBuffer );
        }

        // --- DavidTr: Slide 7(b) ---------------------------------------
        //
        // Pend another read request to get the next client request.
        //

        lpClientContext->LastClientIo = ClientIoRead;
        lpClientContext->BytesReadSoFar = 0;

        bSuccess = ReadFile(
                       (HANDLE)lpClientContext->Socket,
                       lpClientContext->Buffer,
                       sizeof(lpClientContext->Buffer),
                       &dwIoSize,
                       &lpClientContext->Overlapped
                       );
        if ( !bSuccess && GetLastError( ) != ERROR_IO_PENDING ) {
            CloseClient( lpClientContext, FALSE );
            continue;
        }

        //
        // Loop around to get another completed IO request.
        //
    }

    return 0;

} // WorkThread


README.TXT

Simple Service

SUMMARY
=======

The SERVICE sample demonstrates how to create and install a service.

In this particular sample, the service merely opens a named pipe (the name
defaults to \\.\pipe\simple) and waits for read and write operations to the
pipe. If the pipe receives input, it creates the string:

    Hello! [<input goes here>]

and sends it back to the client through the pipe.

The service can be started and stopped from the control panel Services
applet, the net command, or by using the service controller utility (see
MORE INFORMATION).

The service also provides command-line parameters which install, remove, or
run (debug) the service as a console application.

MORE INFORMATION
================

To aid in writing and debugging services, the SDK contains a utility
(MSTOOLS\BIN\SC.EXE) that can be used to control, configure, or obtain
service status. SC displays complete status for any service in the service
database, and allows any of the configuration parameters to be easily
changed at the command line. For more information on SC.EXE, type SC at the
command line.

Usage:

To install the service, first compile everything, and then type:

    simple -install

Now, let's look at SC's command-line parameters:

    sc

To start the service, use the "net start" command, the control panel
Services applet, or the command:

    sc start simpleservice

Verify that the service has entered the RUNNING state:

    sc query simpleservice

Once the service has been started, you can use the CLIENT program to verify
that it really is working, using the syntax:

    client

which should return the response:

    Hello! [World]

If, after playing with the sample, you wish to remove the service, simply
say:

    simple -remove

You may change the name of the pipe by specifying -pipe <pipename> as a
startup parameter for both CLIENT and SIMPLE. The string passed in by CLIENT
can be changed by specifying -string <string>.

Notes:

1) The use of the SERVICE.H header file and the accompanying SERVICE.C file
simplifies the process of writing a service. You as a developer simply need
to follow the TODO's outlined in the header file, and implement the
ServiceStart and ServiceStop functions for your service.

There is no need to modify the code in SERVICE.C. Just add SERVICE.C to your
project and link with the following libraries:

    libcmt.lib
    kernel32.lib
    advapi.lib
    shell32.lib

2) Install/Remove functionality should not be included in a production service.
The functionality is included in this service for illustration purposes, and as
a convenience to developers.


CLIENT.C

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   client.c
//
//  PURPOSE:  This program is a command line oriented
//            demonstration of the Simple service sample.
//
//  FUNCTIONS:
//    main(int argc, char **argv);
//
//  COMMENTS:
//
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

VOID _CRTAPI1 main(int argc, char *argv[])
{
    char    inbuf[80];
    char    outbuf[80];
    DWORD   bytesRead;
    BOOL    ret;
    LPSTR   lpszPipeName = "\\\\.\\pipe\\simple";
    LPSTR   lpszString = "World";
    int     ndx;

    // allow user to define pipe name
    for ( ndx = 1; ndx < argc-1; ndx++ )
    {
        if ( (*argv[ndx] == '-') || (*argv[ndx] == '/') )
        {
            if ( stricmp( "pipe", argv[ndx]+1 ) == 0 )
            {
                lpszPipeName = argv[++ndx];
            }
            else if ( stricmp( "string", argv[ndx]+1 ) == 0 )
            {
                lpszString = argv[++ndx];
            }
            else
            {
                printf("usage: client [-pipe <pipename>] [-string <string>]\n");
                exit(1);
            }
        }
        else
        {
            printf("usage: client [-pipe <pipename>] [-string <string>]\n");
            exit(1);
        }

    }

    strcpy( outbuf, lpszString );

    ret = CallNamedPipeA(lpszPipeName,
                         outbuf, sizeof(outbuf),
                         inbuf, sizeof(inbuf),
                         &bytesRead, NMPWAIT_WAIT_FOREVER);

    if (!ret) {
        printf("client: CallNamedPipe failed, GetLastError = %d\n", GetLastError());
        exit(1);
    }

    printf("client: received: %s\n", inbuf);
}


SERVICE.C

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   service.c
//
//  PURPOSE:  Implements functions required by all services
//            windows.
//
//  FUNCTIONS:
//    main(int argc, char **argv);
//    service_ctrl(DWORD dwCtrlCode);
//    service_main(DWORD dwArgc, LPTSTR *lpszArgv);
//    CmdInstallService();
//    CmdRemoveService();
//    CmdDebugService(int argc, char **argv);
//    ControlHandler ( DWORD dwCtrlType );
//    GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
//
//  COMMENTS:
//
//  AUTHOR: Craig Link - Microsoft Developer Support
//

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <tchar.h>

#include "service.h"

// internal variables
SERVICE_STATUS          ssStatus;       // current status of the service
SERVICE_STATUS_HANDLE   sshStatusHandle;
DWORD                   dwErr = 0;
BOOL                    bDebug = FALSE;
TCHAR                   szErr[256];

// internal function prototypes
VOID WINAPI service_ctrl(DWORD dwCtrlCode);
VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
VOID CmdInstallService();
VOID CmdRemoveService();
VOID CmdDebugService(int argc, char **argv);
BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );

//
//  FUNCTION: main
//
//  PURPOSE: entrypoint for service
//
//  PARAMETERS:
//    argc - number of command line arguments
//    argv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    main() either performs the command line task, or
//    call StartServiceCtrlDispatcher to register the
//    main service thread.  When the this call returns,
//    the service has stopped, so exit.
//
void _CRTAPI1 main(int argc, char **argv)
{
    SERVICE_TABLE_ENTRY dispatchTable[] =
    {
        { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main },
        { NULL, NULL }
    };

    if ( (argc > 1) &&
         ((*argv[1] == '-') || (*argv[1] == '/')) )
    {
        if ( _stricmp( "install", argv[1]+1 ) == 0 )
        {
            CmdInstallService();
        }
        else if ( _stricmp( "remove", argv[1]+1 ) == 0 )
        {
            CmdRemoveService();
        }
        else if ( _stricmp( "debug", argv[1]+1 ) == 0 )
        {
            bDebug = TRUE;
            CmdDebugService(argc, argv);
        }
        else
        {
            goto dispatch;
        }
        exit(0);
    }

    // if it doesn't match any of the above parameters
    // the service control manager may be starting the service
    // so we must call StartServiceCtrlDispatcher
    dispatch:
        // this is just to be friendly
        printf( "%s -install          to install the service\n", SZAPPNAME );
        printf( "%s -remove           to remove the service\n", SZAPPNAME );
        printf( "%s -debug <params>   to run as a console app for debugging\n", SZAPPNAME );
        printf( "\nStartServiceCtrlDispatcher being called.\n" );
        printf( "This may take several seconds.  Please wait.\n" );

        if (!StartServiceCtrlDispatcher(dispatchTable))
            AddToMessageLog(TEXT("StartServiceCtrlDispatcher failed."));
}

//
//  FUNCTION: service_main
//
//  PURPOSE: To perform actual initialization of the service
//
//  PARAMETERS:
//    dwArgc   - number of command line arguments
//    lpszArgv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    This routine performs the service initialization and then calls
//    the user defined ServiceStart() routine to perform majority
//    of the work.
//
void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
{

    // register our service control handler:
    //
    sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);

    if (!sshStatusHandle)
        goto cleanup;

    // SERVICE_STATUS members that don't change in example
    //
    ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ssStatus.dwServiceSpecificExitCode = 0;

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        goto cleanup;

    ServiceStart( dwArgc, lpszArgv );

cleanup:

    // try to report the stopped status to the service control manager.
    //
    if (sshStatusHandle)
        (VOID)ReportStatusToSCMgr(
                            SERVICE_STOPPED,
                            dwErr,
                            0);

    return;
}

//
//  FUNCTION: service_ctrl
//
//  PURPOSE: This function is called by the SCM whenever
//           ControlService() is called on this service.
//
//  PARAMETERS:
//    dwCtrlCode - type of control requested
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
VOID WINAPI service_ctrl(DWORD dwCtrlCode)
{
    // Handle the requested control code.
    //
    switch(dwCtrlCode)
    {
        // Stop the service.
        //
        // SERVICE_STOP_PENDING should be reported before
        // setting the Stop Event - hServerStopEvent - in
        // ServiceStop().  This avoids a race condition
        // which may result in a 1053 - The Service did not respond...
        // error.
        case SERVICE_CONTROL_STOP:
            ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
            ServiceStop();
            return;

        // Update the service status.
        //
        case SERVICE_CONTROL_INTERROGATE:
            break;

        // invalid control code
        //
        default:
            break;

    }

    ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
}

//
//  FUNCTION: ReportStatusToSCMgr()
//
//  PURPOSE: Sets the current status of the service and
//           reports it to the Service Control Manager
//
//  PARAMETERS:
//    dwCurrentState - the state of the service
//    dwWin32ExitCode - error code to report
//    dwWaitHint - worst case estimate to next checkpoint
//
//  RETURN VALUE:
//    TRUE  - success
//    FALSE - failure
//
//  COMMENTS:
//
BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
                         DWORD dwWin32ExitCode,
                         DWORD dwWaitHint)
{
    static DWORD dwCheckPoint = 1;
    BOOL fResult = TRUE;

    if ( !bDebug ) // when debugging we don't report to the SCM
    {
        if (dwCurrentState == SERVICE_START_PENDING)
            ssStatus.dwControlsAccepted = 0;
        else
            ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

        ssStatus.dwCurrentState = dwCurrentState;
        ssStatus.dwWin32ExitCode = dwWin32ExitCode;
        ssStatus.dwWaitHint = dwWaitHint;

        if ( ( dwCurrentState == SERVICE_RUNNING ) ||
             ( dwCurrentState == SERVICE_STOPPED ) )
            ssStatus.dwCheckPoint = 0;
        else
            ssStatus.dwCheckPoint = dwCheckPoint++;

        // Report the status of the service to the service control manager.
        //
        if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) {
            AddToMessageLog(TEXT("SetServiceStatus"));
        }
    }
    return fResult;
}

//
//  FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
//
//  PURPOSE: Allows any thread to log an error message
//
//  PARAMETERS:
//    lpszMsg - text for message
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
VOID AddToMessageLog(LPTSTR lpszMsg)
{
    TCHAR   szMsg[256];
    HANDLE  hEventSource;
    LPTSTR  lpszStrings[2];

    if ( !bDebug )
    {
        dwErr = GetLastError();

        // Use event logging to log the error.
        //
        hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));

        _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
        lpszStrings[0] = szMsg;
        lpszStrings[1] = lpszMsg;

        if (hEventSource != NULL) {
            ReportEvent(hEventSource, // handle of event source
                EVENTLOG_ERROR_TYPE,  // event type
                0,                    // event category
                0,                    // event ID
                NULL,                 // current user's SID
                2,                    // strings in lpszStrings
                0,                    // no bytes of raw data
                lpszStrings,          // array of error strings
                NULL);                // no raw data

            (VOID) DeregisterEventSource(hEventSource);
        }
    }
}

///////////////////////////////////////////////////////////////////
//
//  The following code handles service installation and removal
//

//
//  FUNCTION: CmdInstallService()
//
//  PURPOSE: Installs the service
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void CmdInstallService()
{
    SC_HANDLE   schService;
    SC_HANDLE   schSCManager;

    TCHAR szPath[512];

    if ( GetModuleFileName( NULL, szPath, 512 ) == 0 )
    {
        _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
        return;
    }

    schSCManager = OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
    if ( schSCManager )
    {
        schService = CreateService(
            schSCManager,               // SCManager database
            TEXT(SZSERVICENAME),        // name of service
            TEXT(SZSERVICEDISPLAYNAME), // name to display
            SERVICE_ALL_ACCESS,         // desired access
            SERVICE_WIN32_OWN_PROCESS,  // service type
            SERVICE_DEMAND_START,       // start type
            SERVICE_ERROR_NORMAL,       // error control type
            szPath,                     // service's binary
            NULL,                       // no load ordering group
            NULL,                       // no tag identifier
            TEXT(SZDEPENDENCIES),       // dependencies
            NULL,                       // LocalSystem account
            NULL);                      // no password

        if ( schService )
        {
            _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
            CloseServiceHandle(schService);
        }
        else
        {
            _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
        }

        CloseServiceHandle(schSCManager);
    }
    else
        _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
}

//
//  FUNCTION: CmdRemoveService()
//
//  PURPOSE: Stops and removes the service
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void CmdRemoveService()
{
    SC_HANDLE   schService;
    SC_HANDLE   schSCManager;

    schSCManager = OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
    if ( schSCManager )
    {
        schService = OpenService(schSCManager, TEXT(SZSERVICENAME), SERVICE_ALL_ACCESS);

        if (schService)
        {
            // try to stop the service
            if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
            {
                _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
                Sleep( 1000 );

                while( QueryServiceStatus( schService, &ssStatus ) )
                {
                    if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
                    {
                        _tprintf(TEXT("."));
                        Sleep( 1000 );
                    }
                    else
                        break;
                }

                if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
                    _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
                else
                    _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );

            }

            // now remove the service
            if( DeleteService(schService) )
                _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
            else
                _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));

            CloseServiceHandle(schService);
        }
        else
            _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));

        CloseServiceHandle(schSCManager);
    }
    else
        _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
}

///////////////////////////////////////////////////////////////////
//
//  The following code is for running the service as a console app
//

//
//  FUNCTION: CmdDebugService(int argc, char ** argv)
//
//  PURPOSE: Runs the service as a console application
//
//  PARAMETERS:
//    argc - number of command line arguments
//    argv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void CmdDebugService(int argc, char ** argv)
{
    DWORD dwArgc;
    LPTSTR *lpszArgv;

#ifdef UNICODE
    lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
#else
    dwArgc   = (DWORD) argc;
    lpszArgv = argv;
#endif

    _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));

    SetConsoleCtrlHandler( ControlHandler, TRUE );

    ServiceStart( dwArgc, lpszArgv );
}

//
//  FUNCTION: ControlHandler ( DWORD dwCtrlType )
//
//  PURPOSE: Handled console control events
//
//  PARAMETERS:
//    dwCtrlType - type of control event
//
//  RETURN VALUE:
//    True - handled
//    False - unhandled
//
//  COMMENTS:
//
BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
{
    switch( dwCtrlType )
    {
        case CTRL_BREAK_EVENT:  // use Ctrl+C or Ctrl+Break to simulate
        case CTRL_C_EVENT:      // SERVICE_CONTROL_STOP in debug mode
            _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
            ServiceStop();
            return TRUE;
            break;

    }
    return FALSE;
}

//
//  FUNCTION: GetLastErrorText
//
//  PURPOSE: copies error message text to string
//
//  PARAMETERS:
//    lpszBuf - destination buffer
//    dwSize - size of buffer
//
//  RETURN VALUE:
//    destination buffer
//
//  COMMENTS:
//
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
{
    DWORD dwRet;
    LPTSTR lpszTemp = NULL;

    dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
                           NULL,
                           GetLastError(),
                           LANG_NEUTRAL,
                           (LPTSTR)&lpszTemp,
                           0,
                           NULL );

    // supplied buffer is not long enough
    if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
        lpszBuf[0] = TEXT('\0');
    else
    {
        lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0');  //remove cr and newline character
        _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
    }

    if ( lpszTemp )
        LocalFree((HLOCAL) lpszTemp );

    return lpszBuf;
}


SERVICE.H

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE: service.h
//
//  AUTHOR: Craig Link
//
//
//  Comments:  The use of this header file and the accompanying service.c
//  file simplifies the process of writting a service.  You as a developer
//  simply need to follow the TODO's outlined in this header file, and
//  implement the ServiceStart() and ServiceStop() functions.
//
//  There is no need to modify the code in service.c.  Just add service.c
//  to your project and link with the following libraries...
//
//  libcmt.lib kernel32.lib advapi.lib shell32.lib
//
//  This code also supports unicode.  Be sure to compile both service.c and
//  and code #include "service.h" with the same Unicode setting.
//
//  Upon completion, your code will have the following command line interface
//
//  <service exe> -?                to display this list
//  <service exe> -install          to install the service
//  <service exe> -remove           to remove the service
//  <service exe> -debug <params>   to run as a console app for debugging
//
//  Note: This code also implements Ctrl+C and Ctrl+Break handlers
//        when using the debug option.  These console events cause
//        your ServiceStop routine to be called
//
//        Also, this code only handles the OWN_SERVICE service type
//        running in the LOCAL_SYSTEM security context.
//
//        To control your service ( start, stop, etc ) you may use the
//        Services control panel applet or the NET.EXE program.
//
//        To aid in writing/debugging service, the
//        SDK contains a utility (MSTOOLS\BIN\SC.EXE) that
//        can be used to control, configure, or obtain service status.
//        SC displays complete status for any service/driver
//        in the service database, and allows any of the configuration
//        parameters to be easily changed at the command line.
//        For more information on SC.EXE, type SC at the command line.
//

#ifndef _SERVICE_H
#define _SERVICE_H

#ifdef __cplusplus
extern "C" {
#endif

//////////////////////////////////////////////////////////////////////////////
//// todo: change to desired strings
////
// name of the executable
#define SZAPPNAME            "Simple"
// internal name of the service
#define SZSERVICENAME        "SimpleService"
// displayed name of the service
#define SZSERVICEDISPLAYNAME "Simple Service"
// list of service dependencies - "dep1\0dep2\0\0"
#define SZDEPENDENCIES       ""
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//// todo: ServiceStart()must be defined by in your code.
////       The service should use ReportStatusToSCMgr to indicate
////       progress.  This routine must also be used by StartService()
////       to report to the SCM when the service is running.
////
////       If a ServiceStop procedure is going to take longer than
////       3 seconds to execute, it should spawn a thread to
////       execute the stop code, and return.  Otherwise, the
////       ServiceControlManager will believe that the service has
////       stopped responding
////
VOID ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv);
VOID ServiceStop();
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//// The following are procedures which
//// may be useful to call within the above procedures,
//// but require no implementation by the user.
//// They are implemented in service.c

//
//  FUNCTION: ReportStatusToSCMgr()
//
//  PURPOSE: Sets the current status of the service and
//           reports it to the Service Control Manager
//
//  PARAMETERS:
//    dwCurrentState - the state of the service
//    dwWin32ExitCode - error code to report
//    dwWaitHint - worst case estimate to next checkpoint
//
//  RETURN VALUE:
//    TRUE  - success
//    FALSE - failure
//
BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);

//
//  FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
//
//  PURPOSE: Allows any thread to log an error message
//
//  PARAMETERS:
//    lpszMsg - text for message
//
//  RETURN VALUE:
//    none
//
void AddToMessageLog(LPTSTR lpszMsg);
//////////////////////////////////////////////////////////////////////////////

#ifdef __cplusplus
}
#endif

#endif


SIMPLE.C

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   simple.c
//
//  PURPOSE:  Implements the body of the service.
//            The default behavior is to open a
//            named pipe, \\.\pipe\simple, and read
//            from it.  It the modifies the data and
//            writes it back to the pipe.
//
//  FUNCTIONS:
//            ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv);
//            ServiceStop( );
//
//  COMMENTS: The functions implemented in simple.c are
//            prototyped in service.h
//
//
//  AUTHOR: Craig Link - Microsoft Developer Support
//

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <tchar.h>
#include "service.h"

// this event is signalled when the
// service should end
//
HANDLE  hServerStopEvent = NULL;

//
//  FUNCTION: ServiceStart
//
//  PURPOSE: Actual code of the service
//           that does the work.
//
//  PARAMETERS:
//    dwArgc   - number of command line arguments
//    lpszArgv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    The default behavior is to open a
//    named pipe, \\.\pipe\simple, and read
//    from it.  It the modifies the data and
//    writes it back to the pipe.  The service
//    stops when hServerStopEvent is signalled
//
VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv)
{
    HANDLE                  hPipe = INVALID_HANDLE_VALUE;
    HANDLE                  hEvents[2] = {NULL, NULL};
    OVERLAPPED              os;
    PSECURITY_DESCRIPTOR    pSD = NULL;
    SECURITY_ATTRIBUTES     sa;
    TCHAR                   szIn[80];
    TCHAR                   szOut[80];
    LPTSTR                  lpszPipeName = TEXT("\\\\.\\pipe\\simple");
    BOOL                    bRet;
    DWORD                   cbRead;
    DWORD                   cbWritten;
    DWORD                   dwWait;
    UINT                    ndx;

    ///////////////////////////////////////////////////
    //
    // Service initialization
    //

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        goto cleanup;

    // create the event object. The control handler function signals
    // this event when it receives the "stop" control code.
    //
    hServerStopEvent = CreateEvent(
        NULL,    // no security attributes
        TRUE,    // manual reset event
        FALSE,   // not-signalled
        NULL);   // no name

    if ( hServerStopEvent == NULL)
        goto cleanup;

    hEvents[0] = hServerStopEvent;

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        goto cleanup;

    // create the event object object use in overlapped i/o
    //
    hEvents[1] = CreateEvent(
        NULL,    // no security attributes
        TRUE,    // manual reset event
        FALSE,   // not-signalled
        NULL);   // no name

    if ( hEvents[1] == NULL)
        goto cleanup;

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        goto cleanup;

    // create a security descriptor that allows anyone to write to
    //  the pipe...
    //
    pSD = (PSECURITY_DESCRIPTOR) malloc( SECURITY_DESCRIPTOR_MIN_LENGTH );

    if (pSD == NULL)
        goto cleanup;

    if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
        goto cleanup;

    // add a NULL disc. ACL to the security descriptor.
    //
    if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE))
        goto cleanup;

    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = pSD;
    sa.bInheritHandle = TRUE;

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        goto cleanup;

    // allow user tp define pipe name
    for ( ndx = 1; ndx < dwArgc-1; ndx++ )
    {

        if ( ( (*(lpszArgv[ndx]) == TEXT('-')) ||
               (*(lpszArgv[ndx]) == TEXT('/')) ) &&
             _tcsicmp( TEXT("pipe"), lpszArgv[ndx]+1 ) == 0 )
        {
            lpszPipeName = lpszArgv[++ndx];
        }

    }

    // open our named pipe...
    //
    hPipe = CreateNamedPipe(
                    lpszPipeName         ,  // name of pipe
                    FILE_FLAG_OVERLAPPED |
                    PIPE_ACCESS_DUPLEX,     // pipe open mode
                    PIPE_TYPE_MESSAGE |
                    PIPE_READMODE_MESSAGE |
                    PIPE_WAIT,              // pipe IO type
                    1,                      // number of instances
                    0,                      // size of outbuf (0 == allocate as necessary)
                    0,                      // size of inbuf
                    1000,                   // default time-out value
                    &sa);                   // security attributes

    if (hPipe == INVALID_HANDLE_VALUE) {
        AddToMessageLog(TEXT("Unable to create named pipe"));
        goto cleanup;
    }

    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_RUNNING,       // service state
        NO_ERROR,              // exit code
        0))                    // wait hint
        goto cleanup;

    //
    // End of initialization
    //
    ////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////
    //
    // Service is now running, perform work until shutdown
    //

    while ( 1 )
    {
        // init the overlapped structure
        //
        memset( &os, 0, sizeof(OVERLAPPED) );
        os.hEvent = hEvents[1];
        ResetEvent( hEvents[1] );

        // wait for a connection...
        //
        ConnectNamedPipe(hPipe, &os);

        if ( GetLastError() == ERROR_IO_PENDING )
        {
            dwWait = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE );
            if ( dwWait != WAIT_OBJECT_0+1 )     // not overlapped i/o event - error occurred,
                break;                           // or server stop signaled
        }

        // init the overlapped structure
        //
        memset( &os, 0, sizeof(OVERLAPPED) );
        os.hEvent = hEvents[1];
        ResetEvent( hEvents[1] );

        // grab whatever's coming through the pipe...
        //
        bRet = ReadFile(
                    hPipe,          // file to read from
                    szIn,           // address of input buffer
                    sizeof(szIn),   // number of bytes to read
                    &cbRead,        // number of bytes read
                    &os);           // overlapped stuff, not needed

        if ( !bRet && ( GetLastError() == ERROR_IO_PENDING ) )
        {
            dwWait = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE );
            if ( dwWait != WAIT_OBJECT_0+1 )     // not overlapped i/o event - error occurred,
                break;                           // or server stop signaled
        }

        // munge the string
        //
        _stprintf(szOut, TEXT("Hello! [%s]"), szIn);

        // init the overlapped structure
        //
        memset( &os, 0, sizeof(OVERLAPPED) );
        os.hEvent = hEvents[1];
        ResetEvent( hEvents[1] );

        // send it back out...
        //
        bRet = WriteFile(
                    hPipe,          // file to write to
                    szOut,          // address of output buffer
                    sizeof(szOut),  // number of bytes to write
                    &cbWritten,     // number of bytes written
                    &os);           // overlapped stuff, not needed

        if ( !bRet && ( GetLastError() == ERROR_IO_PENDING ) )
        {
            dwWait = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE );
            if ( dwWait != WAIT_OBJECT_0+1 )     // not overlapped i/o event - error occurred,
                break;                           // or server stop signaled
        }

        // drop the connection...
        //
        DisconnectNamedPipe(hPipe);
    }

  cleanup:

    if (hPipe != INVALID_HANDLE_VALUE )
        CloseHandle(hPipe);

    if (hServerStopEvent)
        CloseHandle(hServerStopEvent);

    if (hEvents[1]) // overlapped i/o event
        CloseHandle(hEvents[1]);

    if ( pSD )
        free( pSD );

}

//
//  FUNCTION: ServiceStop
//
//  PURPOSE: Stops the service
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    If a ServiceStop procedure is going to
//    take longer than 3 seconds to execute,
//    it should spawn a thread to execute the
//    stop code, and return.  Otherwise, the
//    ServiceControlManager will believe that
//    the service has stopped responding.
//
VOID ServiceStop()
{
    if ( hServerStopEvent )
        SetEvent(hServerStopEvent);
}